Go version
go1.25
Output of go env
in your module/workspace:
n/a
What did you do?
type T struct{ calledWith string }
func (t *T) UnmarshalJSON(b []byte) error {
t.calledWith = string(b)
return nil
}
func main() {
var v1, v2 T
in := []byte("0x")
err1 := jsonv1.Unmarshal(in, &v1)
fmt.Println(err1, v1)
err2 := jsonv1in2.Unmarshal(in, &v2)
fmt.Println(err2, v2)
}
What did you see happen?
invalid character 'x' after top-level value {}
invalid character 'x' after top-level value {0}
What did you expect to see?
invalid character 'x' after top-level value {}
invalid character 'x' after top-level value {}
Notice that UnmarshalJSON
was not called for v1, but is called for v1in2.
It seems that v1in2 eagerly handles any valid JSON prefix, and only reports an error for an invalid suffix after evaluation. In contrast, v1 validates the entire JSON input up front.
The v1in2 behavior is an artifact of how it prioritizes streaming unmarshal.
It's unclear if this regression matters. I discovered it based on a test failure, but it has no consequential difference in production.
\cc @neild
Comment From: gabyhelp
Related Issues
- encoding/json: UnmarshalJSON not working consistently with struct embedding #39175 (closed)
- encoding/json: nested unmarshaling mangles errors #68750 (closed)
- encoding/json: unable to set options when unmarshaling #17654 (closed)
- encoding/json/v2: fails to return SyntacticError on empty input #74548 (closed)
- encoding/json: Unmarshal behavior changed to match its documented behavior about not reusing slice elements #39427 (closed)
- encoding/json: UnmarshalTypeError is clobbered when using nested custom marshallers #61337 (closed)
- [encoding/json] JSON unmarshaling does not respect escapes #47458 (closed)
- encoding/json Unmarshals invalid json successfully #33894 (closed)
Related Code Changes
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: neild
I don't think this is a problem. The unmarshal operation correctly fails in both cases, so this really comes down to the exact moment when we discover an error exists. It seems unavoidable that a streaming-capable unmarshal is going to detect errors mid-processing.
I also don't believe I've observed any failures caused by this change in behavior in any Google-internal test, which is a strong hint that nobody is going to be affected by the change.