encoding/json/v2: report EOF for top-level values in UnmarshalDecode

The fully streaming UnmarshalJSONFrom method and UnmarshalFromFunc
introduce an edge case where they can encounter EOF in the stream,
where it should be reported upstream as EOF rather than
ErrUnexpectedEOF or be wrapped within a SemanticError.

This is not possible with other unmarshal methods since the
"json" package would read the appropriate JSON value
before calling the custom method or function.

To avoid custom unmarshal methods from encountering EOF,
check whether the stream is already at EOF for top-level values
before calling the custom method.

Also, when wrapping EOF within a SemanticError, convert it
to ErrUnexpectedEOF to better indicate that this is unexpected.

Fixes #75802

Change-Id: I001396734b7e95b5337f77b71326284974ee730a
Reviewed-on: https://go-review.googlesource.com/c/go/+/710877
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
This commit is contained in:
Joe Tsai 2025-10-10 17:56:04 -07:00 committed by Joseph Tsai
parent 6bcd97d9f4
commit 0e64ee1286
6 changed files with 68 additions and 5 deletions

View file

@ -440,8 +440,9 @@ func UnmarshalRead(in io.Reader, out any, opts ...Options) (err error) {
// Unlike [Unmarshal] and [UnmarshalRead], decode options are ignored because
// they must have already been specified on the provided [jsontext.Decoder].
//
// The input may be a stream of one or more JSON values,
// The input may be a stream of zero or more JSON values,
// where this only unmarshals the next JSON value in the stream.
// If there are no more top-level JSON values, it reports [io.EOF].
// The output must be a non-nil pointer.
// See [Unmarshal] for details about the conversion of JSON into a Go value.
func UnmarshalDecode(in *jsontext.Decoder, out any, opts ...Options) (err error) {