-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
server: Introduce IgnoreUnknownFields option #5
Conversation
I'm not opposed. In fact, I originally implemented it without that restriction. I'd like to think a bit more about whether we can do this without quite so much plumbing. I'll try to follow up when I've had a chance to play with it some more. |
After more thought, I don't think this approach is going to work. The module assumes JSON decoding is context-free, meaning it does not distinguish between "decoding for server X" and "decoding for server Y". Even if we add a flag to the server and plumb it down to the request, there are at least three other places where we decode parameters that do not have access to that flag:
We could address (3) by also adding a client option too, but (1) and (2) are not reachable that way. I can think of a few options to move forward. Not all of these are mutually exclusive:
Note that (1) and (3) are miscible. If the default is strict parsing, we can get non-strict parsing by writing: type T struct { /* … */ }
func (t *T) UnmarshalJSON(bits []byte) error {
type U T
return json.Unmarshal(bits, (*U)(t))
} Working example here: https://play.golang.org/p/dSP28YQAuHM. This is admittedly a little ugly, but would be easy to automate with a func (t *T) UnmarshalJSON(bits []byte) error {
dec := json.NewDecoder(bytes.NewReader(bits))
dec.DisallowUnknownFields()
type U T
return dec.Decode((*U)(t))
} We could hide some of the boilerplate, but I found myself doing this a bunch and I thought that strict should be the default. I don't have any good data to suggest which default is "better" other than my own experience using it. I don't particularly like option (2) at all, but I included it for completeness. Global state has to do some work to justify is existence, and I'm not seeing it here. At minimum it would make the tests a lot more complicated. |
Thank you for explaining your thoughts in such detail. I took some time to think about this too and I agree with your arguments against global state. I first thought that asking consumers of this library to make sure the types they use are either complete or implement custom unmarshaler is a lot to ask. However I then came to the conclusion that it's probably a good thing anyway if the types are expected to be behind and like you said it's not too hard to achieve with I don't have a strong opinion on what should be the default, but I'm leaning more towards the current state. Explicit (opting into unknown fields) is better than implicit. I'm just not sure what's the best place to document this though. |
Addresses #5. When unmarshaling parameters, unknown struct fields are not allowed by the default encoder. Document how this can be overridden by the caller, and add example code to demonstrate the techniques.
I made an attempt in #8, I'd appreciate your feedback if you have time. |
Closing in favour of #8 |
Addresses #5. When unmarshaling parameters, unknown struct fields are not allowed by the default encoder. Document how this can be overridden by the caller, and add example code to demonstrate the techniques.
Commit 78545e2 disallowed unknown struct fields when unmarshaling parameters. The check could be bypassed by implementing the json.Unmarshaler interface, but the need to do so causes friction for implementations of LSP, which has a loose and rapidly-changing schema (see #5, #31). This changes the default back to ignoring unknown fields, and adds a new optional interface to re-enable strict checking without a custom unmarshaler. N.B.: This is a breaking API change. Relevant changes: - Update UnmarshalParams and UnmarshalResult to check for the target having a DisallowUnknownFields method, and to enable the check for such targets. - Rework the decoding to make the default path do less allocation. - Update documentation and tests.
As mentioned in the comment, according to the JSON-RPC specification:
The option therefore allows the server to choose between a strict or tolerant behaviour.
To put this into the context of a real use case:
I'm trying to build a Language Server with this library while also using https://github.com/sourcegraph/go-lsp for the structs. These structs aren't always up to date though and so the client which technically implements the same protocol (likely using a different LSP library and even a different language) may be "more complete". I'd like the Language Server to be tolerant of this.