423 lines
17 KiB
Go
423 lines
17 KiB
Go
// Copyright 2020 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build !goexperiment.jsonv2 || !go1.25
|
|
|
|
package json
|
|
|
|
import (
|
|
"encoding"
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
|
|
"github.com/go-json-experiment/json/internal"
|
|
"github.com/go-json-experiment/json/internal/jsonflags"
|
|
"github.com/go-json-experiment/json/internal/jsonopts"
|
|
"github.com/go-json-experiment/json/internal/jsonwire"
|
|
"github.com/go-json-experiment/json/jsontext"
|
|
)
|
|
|
|
var errNonStringValue = errors.New("JSON value must be string type")
|
|
|
|
// Interfaces for custom serialization.
|
|
var (
|
|
jsonMarshalerType = reflect.TypeFor[Marshaler]()
|
|
jsonMarshalerToType = reflect.TypeFor[MarshalerTo]()
|
|
jsonUnmarshalerType = reflect.TypeFor[Unmarshaler]()
|
|
jsonUnmarshalerFromType = reflect.TypeFor[UnmarshalerFrom]()
|
|
textAppenderType = reflect.TypeFor[encoding.TextAppender]()
|
|
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
|
|
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
|
|
|
|
allMarshalerTypes = []reflect.Type{jsonMarshalerToType, jsonMarshalerType, textAppenderType, textMarshalerType}
|
|
allUnmarshalerTypes = []reflect.Type{jsonUnmarshalerFromType, jsonUnmarshalerType, textUnmarshalerType}
|
|
allMethodTypes = append(allMarshalerTypes, allUnmarshalerTypes...)
|
|
)
|
|
|
|
// Marshaler is implemented by types that can marshal themselves.
|
|
// It is recommended that types implement [MarshalerTo] unless the implementation
|
|
// is trying to avoid directly depending on the "jsontext" package.
|
|
//
|
|
// Implementations should return a buffer that is safe
|
|
// for the caller to retain and potentially mutate.
|
|
//
|
|
// Implementations must not return [errors.ErrUnsupported].
|
|
//
|
|
// If the returned error is a [SemanticError], then unpopulated fields
|
|
// of the error may be populated by [json] with additional context.
|
|
// Errors of other types are wrapped within a [SemanticError].
|
|
//
|
|
// Implementations should assume [Deterministic] is true and return
|
|
// deterministic output.
|
|
type Marshaler interface {
|
|
MarshalJSON() ([]byte, error)
|
|
}
|
|
|
|
// MarshalerTo is implemented by types that can marshal themselves.
|
|
// It is recommended that types implement MarshalerTo instead of [Marshaler]
|
|
// since it is both more performant and more flexible.
|
|
// If a type implements both Marshaler and MarshalerTo,
|
|
// then MarshalerTo takes precedence. In such a case, both implementations
|
|
// should aim to have equivalent behavior for the default marshal options.
|
|
//
|
|
// The implementation must write only one JSON value to the Encoder.
|
|
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
|
// the Encoder. The "json" package calling the method will
|
|
// use the next available JSON representation for the receiver type,
|
|
// as described in [Marshal].
|
|
// Implementations must not retain the pointer to [jsontext.Encoder].
|
|
//
|
|
// If the returned error is a [SemanticError], then unpopulated fields
|
|
// of the error may be populated by [json] with additional context.
|
|
// Errors of other types are wrapped within a [SemanticError],
|
|
// except for IO errors.
|
|
//
|
|
// The MarshalJSONTo method should not be called directly as it may
|
|
// return sentinel errors that need special handling.
|
|
// Users should instead call [MarshalEncode], which handles such cases.
|
|
//
|
|
// Implementations should inspect the marshal options from
|
|
// [jsontext.Encoder.Options] and adjust behavior to respect the options as
|
|
// necessary.
|
|
//
|
|
// The following options may be relevant to MarshalerTo implementations:
|
|
//
|
|
// - [Deterministic]: if the implementation may produce non-deterministic output
|
|
// - [StringifyNumbers]: if the type is represented as a JSON number
|
|
//
|
|
// Several options, such as [FormatNilSliceAsNull], apply only to native Go
|
|
// types. Thus, these options are typically not directly relevant to
|
|
// MarshalerTo implementations. However, types representing a composite type
|
|
// should marshal contained types using [MarshalEncode] to ensure these options
|
|
// apply to the contained types. Similarly, [WithMarshalers] may influence
|
|
// marshaling of any contained type within a composite type.
|
|
//
|
|
// All other options are automatically handled outside of the MarshalerTo
|
|
// implementation, and thus are not relevant to implementations.
|
|
type MarshalerTo interface {
|
|
MarshalJSONTo(*jsontext.Encoder) error
|
|
}
|
|
|
|
// Unmarshaler is implemented by types that can unmarshal themselves.
|
|
// It is recommended that types implement [UnmarshalerFrom] unless the implementation
|
|
// is trying to avoid a direct dependency on the "jsontext" package.
|
|
//
|
|
// The input can be assumed to be a valid encoding of a JSON value
|
|
// if called from unmarshal functionality in this package.
|
|
// It is recommended that UnmarshalJSON implement merge semantics
|
|
// when unmarshaling into a pre-populated value, as described in [Unmarshal].
|
|
//
|
|
// Implementations must not retain or mutate the input []byte.
|
|
//
|
|
// Implementations must not return [errors.ErrUnsupported].
|
|
//
|
|
// If the returned error is a [SemanticError], then unpopulated fields
|
|
// of the error may be populated by [json] with additional context.
|
|
// Errors of other types are wrapped within a [SemanticError].
|
|
type Unmarshaler interface {
|
|
UnmarshalJSON([]byte) error
|
|
}
|
|
|
|
// UnmarshalerFrom is implemented by types that can unmarshal themselves.
|
|
// It is recommended that types implement UnmarshalerFrom instead of [Unmarshaler]
|
|
// since this is both more performant and more flexible.
|
|
// If a type implements both Unmarshaler and UnmarshalerFrom,
|
|
// then UnmarshalerFrom takes precedence. In such a case, both implementations
|
|
// should aim to have equivalent behavior for the default unmarshal options.
|
|
//
|
|
// The implementation must read only one JSON value from the Decoder.
|
|
// It is recommended that UnmarshalJSONFrom implement merge semantics when
|
|
// unmarshaling into a pre-populated value, as described in [Unmarshal].
|
|
// Alternatively, it may return [errors.ErrUnsupported] without mutating
|
|
// the Decoder. The "json" package calling the method will
|
|
// use the next available JSON representation for the receiver type.
|
|
// Implementations must not retain the pointer to [jsontext.Decoder].
|
|
//
|
|
// If the returned error is a [SemanticError], then unpopulated fields
|
|
// of the error may be populated by [json] with additional context.
|
|
// Errors of other types are wrapped within a [SemanticError],
|
|
// except for [jsontext.SyntacticError]s and IO errors.
|
|
//
|
|
// The UnmarshalJSONFrom method should not be called directly as it may
|
|
// return sentinel errors that need special handling.
|
|
// Users should instead call [UnmarshalDecode], which handles such cases.
|
|
//
|
|
// Implementations should inspect the unmarshal options from
|
|
// [jsontext.Decoder.Options] and adjust behavior to respect the options as
|
|
// necessary.
|
|
//
|
|
// The following options may be relevant to UnmarshalerFrom implementations:
|
|
//
|
|
// - [StringifyNumbers]: if the type is represented as a JSON number
|
|
//
|
|
// Several options, such as [FormatNilSliceAsNull], apply only to native Go
|
|
// types. Thus, these options are typically not directly relevant to
|
|
// UnmarshalerFrom implementations. However, types representing a composite
|
|
// type should unmarshal contained types using [UnmarshalDecode] to ensure
|
|
// these options apply to the contained types. Similarly, [WithUnmarshalers]
|
|
// may influence unmarshaling of any contained type within a composite type.
|
|
//
|
|
// All other options are automatically handled outside of the UnmarshalerFrom
|
|
// implementation, and thus are not relevant to implementations.
|
|
type UnmarshalerFrom interface {
|
|
UnmarshalJSONFrom(*jsontext.Decoder) error
|
|
}
|
|
|
|
func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
|
|
// Avoid injecting method arshaler on the pointer or interface version
|
|
// to avoid ever calling the method on a nil pointer or interface receiver.
|
|
// Let it be injected on the value receiver (which is always addressable).
|
|
if t.Kind() == reflect.Pointer || t.Kind() == reflect.Interface {
|
|
return fncs
|
|
}
|
|
|
|
if needAddr, ok := implements(t, textMarshalerType); ok {
|
|
fncs.nonDefault = true
|
|
prevMarshal := fncs.marshal
|
|
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
|
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
(needAddr && va.forcedAddr) {
|
|
return prevMarshal(enc, va, mo)
|
|
}
|
|
marshaler, _ := reflect.TypeAssert[encoding.TextMarshaler](va.Addr())
|
|
if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
|
|
b2, err := marshaler.MarshalText()
|
|
return append(b, b2...), err
|
|
}); err != nil {
|
|
err = wrapErrUnsupported(err, "MarshalText method")
|
|
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalText") // unlike unmarshal, always wrapped
|
|
}
|
|
if !isSemanticError(err) && !export.IsIOError(err) {
|
|
err = newMarshalErrorBefore(enc, t, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if needAddr, ok := implements(t, textAppenderType); ok {
|
|
fncs.nonDefault = true
|
|
prevMarshal := fncs.marshal
|
|
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
|
|
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
(needAddr && va.forcedAddr) {
|
|
return prevMarshal(enc, va, mo)
|
|
}
|
|
appender, _ := reflect.TypeAssert[encoding.TextAppender](va.Addr())
|
|
if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
|
|
err = wrapErrUnsupported(err, "AppendText method")
|
|
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return internal.NewMarshalerError(va.Addr().Interface(), err, "AppendText") // unlike unmarshal, always wrapped
|
|
}
|
|
if !isSemanticError(err) && !export.IsIOError(err) {
|
|
err = newMarshalErrorBefore(enc, t, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if needAddr, ok := implements(t, jsonMarshalerType); ok {
|
|
fncs.nonDefault = true
|
|
prevMarshal := fncs.marshal
|
|
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
|
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
|
|
return prevMarshal(enc, va, mo)
|
|
}
|
|
marshaler, _ := reflect.TypeAssert[Marshaler](va.Addr())
|
|
val, err := marshaler.MarshalJSON()
|
|
if err != nil {
|
|
err = wrapErrUnsupported(err, "MarshalJSON method")
|
|
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
|
|
}
|
|
err = newMarshalErrorBefore(enc, t, err)
|
|
return collapseSemanticErrors(err)
|
|
}
|
|
if err := enc.WriteValue(val); err != nil {
|
|
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
|
|
}
|
|
if isSyntacticError(err) {
|
|
err = newMarshalErrorBefore(enc, t, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if needAddr, ok := implements(t, jsonMarshalerToType); ok {
|
|
fncs.nonDefault = true
|
|
prevMarshal := fncs.marshal
|
|
fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
|
|
if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
|
|
return prevMarshal(enc, va, mo)
|
|
}
|
|
xe := export.Encoder(enc)
|
|
prevDepth, prevLength := xe.Tokens.DepthLength()
|
|
xe.Flags.Set(jsonflags.WithinArshalCall | 1)
|
|
marshaler, _ := reflect.TypeAssert[MarshalerTo](va.Addr())
|
|
err := marshaler.MarshalJSONTo(enc)
|
|
xe.Flags.Set(jsonflags.WithinArshalCall | 0)
|
|
currDepth, currLength := xe.Tokens.DepthLength()
|
|
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
|
err = errNonSingularValue
|
|
}
|
|
if err != nil {
|
|
if errors.Is(err, errors.ErrUnsupported) {
|
|
if prevDepth == currDepth && prevLength == currLength {
|
|
return prevMarshal(enc, va, mo)
|
|
}
|
|
err = errUnsupportedMutation
|
|
}
|
|
if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSONTo") // unlike unmarshal, always wrapped
|
|
}
|
|
if !export.IsIOError(err) {
|
|
err = newSemanticErrorWithPosition(enc, t, prevDepth, prevLength, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if _, ok := implements(t, textUnmarshalerType); ok {
|
|
fncs.nonDefault = true
|
|
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
|
xd := export.Decoder(dec)
|
|
var flags jsonwire.ValueFlags
|
|
val, err := xd.ReadValue(&flags)
|
|
if err != nil {
|
|
return err // must be a syntactic or I/O error
|
|
}
|
|
if val.Kind() == 'n' {
|
|
if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
|
|
va.SetZero()
|
|
}
|
|
return nil
|
|
}
|
|
if val.Kind() != '"' {
|
|
return newUnmarshalErrorAfter(dec, t, errNonStringValue)
|
|
}
|
|
s := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
|
|
unmarshaler, _ := reflect.TypeAssert[encoding.TextUnmarshaler](va.Addr())
|
|
if err := unmarshaler.UnmarshalText(s); err != nil {
|
|
err = wrapErrUnsupported(err, "UnmarshalText method")
|
|
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return err // unlike marshal, never wrapped
|
|
}
|
|
if !isSemanticError(err) && !isSyntacticError(err) && !export.IsIOError(err) {
|
|
err = newUnmarshalErrorAfter(dec, t, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if _, ok := implements(t, jsonUnmarshalerType); ok {
|
|
fncs.nonDefault = true
|
|
prevUnmarshal := fncs.unmarshal
|
|
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
|
if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
export.Decoder(dec).Tokens.Last.NeedObjectName() {
|
|
return prevUnmarshal(dec, va, uo)
|
|
}
|
|
val, err := dec.ReadValue()
|
|
if err != nil {
|
|
return err // must be a syntactic or I/O error
|
|
}
|
|
unmarshaler, _ := reflect.TypeAssert[Unmarshaler](va.Addr())
|
|
if err := unmarshaler.UnmarshalJSON(val); err != nil {
|
|
err = wrapErrUnsupported(err, "UnmarshalJSON method")
|
|
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
return err // unlike marshal, never wrapped
|
|
}
|
|
err = newUnmarshalErrorAfter(dec, t, err)
|
|
return collapseSemanticErrors(err)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if _, ok := implements(t, jsonUnmarshalerFromType); ok {
|
|
fncs.nonDefault = true
|
|
prevUnmarshal := fncs.unmarshal
|
|
fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
|
|
if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
|
|
export.Decoder(dec).Tokens.Last.NeedObjectName() {
|
|
return prevUnmarshal(dec, va, uo)
|
|
}
|
|
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)
|
|
xd.Flags.Set(jsonflags.WithinArshalCall | 0)
|
|
currDepth, currLength := xd.Tokens.DepthLength()
|
|
if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
|
|
err = errNonSingularValue
|
|
}
|
|
if err != nil {
|
|
if errors.Is(err, errors.ErrUnsupported) {
|
|
if prevDepth == currDepth && prevLength == currLength {
|
|
return prevUnmarshal(dec, va, uo)
|
|
}
|
|
err = errUnsupportedMutation
|
|
}
|
|
if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
|
if err2 := xd.SkipUntil(prevDepth, prevLength+1); err2 != nil {
|
|
return err2
|
|
}
|
|
return err // unlike marshal, never wrapped
|
|
}
|
|
if !isSyntacticError(err) && !export.IsIOError(err) {
|
|
err = newSemanticErrorWithPosition(dec, t, prevDepth, prevLength, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fncs
|
|
}
|
|
|
|
// implementsAny is like t.Implements(ifaceType) for a list of interfaces,
|
|
// but checks whether either t or reflect.PointerTo(t) implements the interface.
|
|
func implementsAny(t reflect.Type, ifaceTypes ...reflect.Type) bool {
|
|
for _, ifaceType := range ifaceTypes {
|
|
if _, ok := implements(t, ifaceType); ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// implements is like t.Implements(ifaceType) but checks whether
|
|
// either t or reflect.PointerTo(t) implements the interface.
|
|
// It also reports whether the value needs to be addressed
|
|
// in order to satisfy the interface.
|
|
func implements(t, ifaceType reflect.Type) (needAddr, ok bool) {
|
|
switch {
|
|
case t.Implements(ifaceType):
|
|
return false, true
|
|
case reflect.PointerTo(t).Implements(ifaceType):
|
|
return true, true
|
|
default:
|
|
return false, false
|
|
}
|
|
}
|