| | |
| | |
| | |
| |
|
| | |
| |
|
| | package json |
| |
|
| | import ( |
| | "cmp" |
| | "errors" |
| | "fmt" |
| | "io" |
| | "reflect" |
| | "strconv" |
| | "strings" |
| | "sync" |
| |
|
| | "encoding/json/internal/jsonflags" |
| | "encoding/json/internal/jsonopts" |
| | "encoding/json/internal/jsonwire" |
| | "encoding/json/jsontext" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | var ErrUnknownName = errors.New("unknown object member name") |
| |
|
| | const errorPrefix = "json: " |
| |
|
| | func isSemanticError(err error) bool { |
| | _, ok := err.(*SemanticError) |
| | return ok |
| | } |
| |
|
| | func isSyntacticError(err error) bool { |
| | _, ok := err.(*jsontext.SyntacticError) |
| | return ok |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func isFatalError(err error, flags jsonflags.Flags) bool { |
| | return !flags.Get(jsonflags.ReportErrorsWithLegacySemantics) || |
| | isSyntacticError(err) || export.IsIOError(err) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | type SemanticError struct { |
| | requireKeyedLiterals |
| | nonComparable |
| |
|
| | action string |
| |
|
| | |
| | ByteOffset int64 |
| | |
| | |
| | JSONPointer jsontext.Pointer |
| |
|
| | |
| | JSONKind jsontext.Kind |
| | |
| | |
| | JSONValue jsontext.Value |
| | |
| | GoType reflect.Type |
| |
|
| | |
| | Err error |
| | } |
| |
|
| | |
| | type coder interface { |
| | StackPointer() jsontext.Pointer |
| | Options() Options |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func newInvalidFormatError(c coder, t reflect.Type) error { |
| | err := fmt.Errorf("invalid format flag %q", c.Options().(*jsonopts.Struct).Format) |
| | switch c := c.(type) { |
| | case *jsontext.Encoder: |
| | err = newMarshalErrorBefore(c, t, err) |
| | case *jsontext.Decoder: |
| | err = newUnmarshalErrorBeforeWithSkipping(c, t, err) |
| | } |
| | return err |
| | } |
| |
|
| | |
| | |
| | func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error { |
| | 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))} |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) error { |
| | var k jsontext.Kind |
| | if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { |
| | k = d.PeekKind() |
| | } |
| | 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} |
| | } |
| |
|
| | |
| | |
| | |
| | func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, t reflect.Type, err error) error { |
| | err = newUnmarshalErrorBefore(d, t, err) |
| | if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { |
| | if err2 := export.Decoder(d).SkipValue(); err2 != nil { |
| | return err2 |
| | } |
| | } |
| | return err |
| | } |
| |
|
| | |
| | |
| | func newUnmarshalErrorAfter(d *jsontext.Decoder, t reflect.Type, err error) error { |
| | tokOrVal := export.Decoder(d).PreviousTokenOrValue() |
| | 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()} |
| | } |
| |
|
| | |
| | |
| | |
| | func newUnmarshalErrorAfterWithValue(d *jsontext.Decoder, t reflect.Type, err error) error { |
| | serr := newUnmarshalErrorAfter(d, t, err).(*SemanticError) |
| | if serr.JSONKind == '"' || serr.JSONKind == '0' { |
| | serr.JSONValue = jsontext.Value(export.Decoder(d).PreviousTokenOrValue()).Clone() |
| | } |
| | return serr |
| | } |
| |
|
| | |
| | |
| | |
| | func newUnmarshalErrorAfterWithSkipping(d *jsontext.Decoder, t reflect.Type, err error) error { |
| | err = newUnmarshalErrorAfter(d, t, err) |
| | if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { |
| | if err2 := export.Decoder(d).SkipValueRemainder(); err2 != nil { |
| | return err2 |
| | } |
| | } |
| | return err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func newSemanticErrorWithPosition(c coder, t reflect.Type, prevDepth int, prevLength int64, err error) error { |
| | serr, _ := err.(*SemanticError) |
| | 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 } |
| | var offset int64 |
| | switch c := c.(type) { |
| | case *jsontext.Encoder: |
| | e := export.Encoder(c) |
| | serr.action = cmp.Or(serr.action, "marshal") |
| | currDepth, currLength = e.Tokens.DepthLength() |
| | offset = c.OutputOffset() + int64(export.Encoder(c).CountNextDelimWhitespace()) |
| | coderState = e |
| | case *jsontext.Decoder: |
| | d := export.Decoder(c) |
| | serr.action = cmp.Or(serr.action, "unmarshal") |
| | currDepth, currLength = d.Tokens.DepthLength() |
| | tokOrVal := d.PreviousTokenOrValue() |
| | offset = c.InputOffset() - int64(len(tokOrVal)) |
| | if (prevDepth == currDepth && prevLength == currLength) || len(tokOrVal) == 0 { |
| | |
| | |
| | offset = c.InputOffset() + int64(export.Decoder(c).CountNextDelimWhitespace()) |
| | } |
| | coderState = d |
| | } |
| | serr.ByteOffset = cmp.Or(serr.ByteOffset, offset) |
| | if serr.JSONPointer == "" { |
| | where := 0 |
| | switch { |
| | case prevDepth == currDepth && prevLength+0 == currLength: |
| | where = +1 |
| | case prevDepth == currDepth && prevLength+1 == currLength: |
| | where = -1 |
| | } |
| | serr.JSONPointer = jsontext.Pointer(coderState.AppendStackPointer(nil, where)) |
| | } |
| | serr.GoType = cmp.Or(serr.GoType, t) |
| | return serr |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func collapseSemanticErrors(err error) error { |
| | if serr1, ok := err.(*SemanticError); ok { |
| | if serr2, ok := serr1.Err.(*SemanticError); ok { |
| | serr2.ByteOffset = serr1.ByteOffset + serr2.ByteOffset |
| | serr2.JSONPointer = serr1.JSONPointer + serr2.JSONPointer |
| | *serr1 = *serr2 |
| | } |
| | } |
| | return err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | var errorModalVerb = sync.OnceValue(func() string { |
| | for phrase := range map[string]struct{}{"cannot": {}, "unable to": {}} { |
| | return phrase |
| | } |
| | return "" |
| | }) |
| |
|
| | func (e *SemanticError) Error() string { |
| | var sb strings.Builder |
| | sb.WriteString(errorPrefix) |
| | sb.WriteString(errorModalVerb()) |
| |
|
| | |
| | var preposition string |
| | switch e.action { |
| | case "marshal": |
| | sb.WriteString(" marshal") |
| | preposition = " from" |
| | case "unmarshal": |
| | sb.WriteString(" unmarshal") |
| | preposition = " into" |
| | default: |
| | sb.WriteString(" handle") |
| | preposition = " with" |
| | } |
| |
|
| | |
| | switch e.JSONKind { |
| | case 'n': |
| | sb.WriteString(" JSON null") |
| | case 'f', 't': |
| | sb.WriteString(" JSON boolean") |
| | case '"': |
| | sb.WriteString(" JSON string") |
| | case '0': |
| | sb.WriteString(" JSON number") |
| | case '{', '}': |
| | sb.WriteString(" JSON object") |
| | case '[', ']': |
| | sb.WriteString(" JSON array") |
| | default: |
| | if e.action == "" { |
| | preposition = "" |
| | } |
| | } |
| | if len(e.JSONValue) > 0 && len(e.JSONValue) < 100 { |
| | sb.WriteByte(' ') |
| | sb.Write(e.JSONValue) |
| | } |
| |
|
| | |
| | if e.GoType != nil { |
| | typeString := e.GoType.String() |
| | if len(typeString) > 100 { |
| | |
| | |
| | |
| | |
| | |
| | typeString = e.GoType.Kind().String() |
| | if e.GoType.Kind() == reflect.Struct && e.GoType.Name() == "" { |
| | for i := range e.GoType.NumField() { |
| | if pkgPath := e.GoType.Field(i).PkgPath; pkgPath != "" { |
| | typeString = pkgPath[strings.LastIndexByte(pkgPath, '/')+len("/"):] + ".struct" |
| | break |
| | } |
| | } |
| | } |
| | } |
| | sb.WriteString(preposition) |
| | sb.WriteString(" Go ") |
| | sb.WriteString(typeString) |
| | } |
| |
|
| | |
| | if e.Err == ErrUnknownName { |
| | sb.WriteString(": ") |
| | sb.WriteString(ErrUnknownName.Error()) |
| | sb.WriteString(" ") |
| | sb.WriteString(strconv.Quote(e.JSONPointer.LastToken())) |
| | if parent := e.JSONPointer.Parent(); parent != "" { |
| | sb.WriteString(" within ") |
| | sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(parent), 100))) |
| | } |
| | return sb.String() |
| | } |
| |
|
| | |
| | |
| | switch serr, _ := e.Err.(*jsontext.SyntacticError); { |
| | case e.JSONPointer != "": |
| | if serr == nil || !e.JSONPointer.Contains(serr.JSONPointer) { |
| | sb.WriteString(" within ") |
| | sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(e.JSONPointer), 100))) |
| | } |
| | case e.ByteOffset > 0: |
| | if serr == nil || !(e.ByteOffset <= serr.ByteOffset) { |
| | sb.WriteString(" after offset ") |
| | sb.WriteString(strconv.FormatInt(e.ByteOffset, 10)) |
| | } |
| | } |
| |
|
| | |
| | if e.Err != nil { |
| | errString := e.Err.Error() |
| | if isSyntacticError(e.Err) { |
| | errString = strings.TrimPrefix(errString, "jsontext: ") |
| | } |
| | sb.WriteString(": ") |
| | sb.WriteString(errString) |
| | } |
| |
|
| | return sb.String() |
| | } |
| |
|
| | func (e *SemanticError) Unwrap() error { |
| | return e.Err |
| | } |
| |
|
| | func newDuplicateNameError(ptr jsontext.Pointer, quotedName []byte, offset int64) error { |
| | if quotedName != nil { |
| | name, _ := jsonwire.AppendUnquote(nil, quotedName) |
| | ptr = ptr.AppendToken(string(name)) |
| | } |
| | return &jsontext.SyntacticError{ |
| | ByteOffset: offset, |
| | JSONPointer: ptr, |
| | Err: jsontext.ErrDuplicateName, |
| | } |
| | } |
| |
|
| | |
| | func toUnexpectedEOF(err error) error { |
| | if err == io.EOF { |
| | return io.ErrUnexpectedEOF |
| | } |
| | return err |
| | } |
| |
|