return nil
}
+// AtEOF reports whether the decoder is at EOF.
+func (d *decoderState) AtEOF() bool {
+ _, err := d.consumeWhitespace(d.prevEnd)
+ return err == io.ErrUnexpectedEOF
+}
+
// CheckEOF verifies that the input has no more data.
func (d *decoderState) CheckEOF() error {
return d.checkEOF(d.prevEnd)
// 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) {
import (
"errors"
"fmt"
+ "io"
"reflect"
"sync"
fnc: func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
xd := export.Decoder(dec)
prevDepth, prevLength := xd.Tokens.DepthLength()
+ if prevDepth == 1 && xd.AtEOF() {
+ return io.EOF // check EOF early to avoid fn reporting an EOF
+ }
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
v, _ := reflect.TypeAssert[T](va.castTo(t))
err := fn(dec, v)
import (
"encoding"
"errors"
+ "io"
"reflect"
"encoding/json/internal"
}
xd := export.Decoder(dec)
prevDepth, prevLength := xd.Tokens.DepthLength()
+ if prevDepth == 1 && xd.AtEOF() {
+ return io.EOF // check EOF early to avoid fn reporting an EOF
+ }
xd.Flags.Set(jsonflags.WithinArshalCall | 1)
unmarshaler, _ := reflect.TypeAssert[UnmarshalerFrom](va.Addr())
err := unmarshaler.UnmarshalJSONFrom(dec)
})),
wantErr: EU(errSomeError).withType(0, T[unmarshalJSONv2Func]()),
}, {
- name: jsontest.Name("Methods/Invalid/JSONv2/TooFew"),
+ name: jsontest.Name("Methods/Invalid/JSONv2/TooFew"),
+ inBuf: `{}`,
inVal: addr(unmarshalJSONv2Func(func(*jsontext.Decoder) error {
return nil // do nothing
})),
})
}
+type unmarshalerEOF struct{}
+
+func (unmarshalerEOF) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
+ return io.EOF // should be wrapped and converted by Unmarshal to io.ErrUnexpectedEOF
+}
+
+// TestUnmarshalEOF verifies that io.EOF is only ever returned by
+// UnmarshalDecode for a top-level value.
+func TestUnmarshalEOF(t *testing.T) {
+ opts := WithUnmarshalers(UnmarshalFromFunc(func(dec *jsontext.Decoder, _ *struct{}) error {
+ return io.EOF // should be wrapped and converted by Unmarshal to io.ErrUnexpectedEOF
+ }))
+
+ for _, in := range []string{"", "[", "[null", "[null]"} {
+ for _, newOut := range []func() any{
+ func() any { return new(unmarshalerEOF) },
+ func() any { return new([]unmarshalerEOF) },
+ func() any { return new(struct{}) },
+ func() any { return new([]struct{}) },
+ } {
+ wantErr := io.ErrUnexpectedEOF
+ if gotErr := Unmarshal([]byte(in), newOut(), opts); !errors.Is(gotErr, wantErr) {
+ t.Errorf("Unmarshal = %v, want %v", gotErr, wantErr)
+ }
+ if gotErr := UnmarshalRead(strings.NewReader(in), newOut(), opts); !errors.Is(gotErr, wantErr) {
+ t.Errorf("Unmarshal = %v, want %v", gotErr, wantErr)
+ }
+ switch gotErr := UnmarshalDecode(jsontext.NewDecoder(strings.NewReader(in)), newOut(), opts); {
+ case in != "" && !errors.Is(gotErr, wantErr):
+ t.Errorf("Unmarshal = %v, want %v", gotErr, wantErr)
+ case in == "" && gotErr != io.EOF:
+ t.Errorf("Unmarshal = %v, want %v", gotErr, io.EOF)
+ }
+ }
+ }
+}
+
type ReaderFunc func([]byte) (int, error)
func (f ReaderFunc) Read(b []byte) (int, error) { return f(b) }
"cmp"
"errors"
"fmt"
+ "io"
"reflect"
"strconv"
"strings"
// newMarshalErrorBefore wraps err in a SemanticError assuming that e
// is positioned right before the next token or value, which causes an error.
func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error {
- return &SemanticError{action: "marshal", GoType: t, Err: err,
+ return &SemanticError{action: "marshal", GoType: t, Err: toUnexpectedEOF(err),
ByteOffset: e.OutputOffset() + int64(export.Encoder(e).CountNextDelimWhitespace()),
JSONPointer: jsontext.Pointer(export.Encoder(e).AppendStackPointer(nil, +1))}
}
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
k = d.PeekKind()
}
- return &SemanticError{action: "unmarshal", GoType: t, Err: err,
+ return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
ByteOffset: d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1)),
JSONKind: k}
// is positioned right after the previous token or value, which caused an error.
func newUnmarshalErrorAfter(d *jsontext.Decoder, t reflect.Type, err error) error {
tokOrVal := export.Decoder(d).PreviousTokenOrValue()
- return &SemanticError{action: "unmarshal", GoType: t, Err: err,
+ return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
ByteOffset: d.InputOffset() - int64(len(tokOrVal)),
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, -1)),
JSONKind: jsontext.Value(tokOrVal).Kind()}
if serr == nil {
serr = &SemanticError{Err: err}
}
+ serr.Err = toUnexpectedEOF(serr.Err)
var currDepth int
var currLength int64
var coderState interface{ AppendStackPointer([]byte, int) []byte }
Err: jsontext.ErrDuplicateName,
}
}
+
+// toUnexpectedEOF converts [io.EOF] to [io.ErrUnexpectedEOF].
+func toUnexpectedEOF(err error) error {
+ if err == io.EOF {
+ return io.ErrUnexpectedEOF
+ }
+ return err
+}