package json
import (
"encoding"
"errors"
"reflect"
"encoding/json/internal"
"encoding/json/internal/jsonflags"
"encoding/json/internal/jsonopts"
"encoding/json/internal/jsonwire"
"encoding/json/jsontext"
)
var errNonStringValue = errors .New ("JSON value must be string type" )
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 ...)
)
type Marshaler interface {
MarshalJSON () ([]byte , error )
}
type MarshalerTo interface {
MarshalJSONTo (*jsontext .Encoder ) error
}
type Unmarshaler interface {
UnmarshalJSON ([]byte ) error
}
type UnmarshalerFrom interface {
UnmarshalJSONFrom (*jsontext .Decoder ) error
}
func makeMethodArshaler(fncs *arshaler , t reflect .Type ) *arshaler {
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 := va .Addr ().Interface ().(encoding .TextMarshaler )
if err := export .Encoder (enc ).AppendRaw ('"' , false , func (b []byte ) ([]byte , error ) {
b2 , err := marshaler .MarshalText ()
return append (b , b2 ...), err
}); err != nil {
err = wrapSkipFunc (err , "marshal method" )
if mo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return internal .NewMarshalerError (va .Addr ().Interface (), err , "MarshalText" )
}
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 := va .Addr ().Interface ().(encoding .TextAppender )
if err := export .Encoder (enc ).AppendRaw ('"' , false , appender .AppendText ); err != nil {
err = wrapSkipFunc (err , "append method" )
if mo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return internal .NewMarshalerError (va .Addr ().Interface (), err , "AppendText" )
}
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 := va .Addr ().Interface ().(Marshaler )
val , err := marshaler .MarshalJSON ()
if err != nil {
err = wrapSkipFunc (err , "marshal method" )
if mo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return internal .NewMarshalerError (va .Addr ().Interface (), err , "MarshalJSON" )
}
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" )
}
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 )
err := va .Addr ().Interface ().(MarshalerTo ).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 {
err = wrapSkipFunc (err , "marshal method" )
if mo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return internal .NewMarshalerError (va .Addr ().Interface (), err , "MarshalJSONTo" )
}
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
}
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 := va .Addr ().Interface ().(encoding .TextUnmarshaler )
if err := unmarshaler .UnmarshalText (s ); err != nil {
err = wrapSkipFunc (err , "unmarshal method" )
if uo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return err
}
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
}
unmarshaler := va .Addr ().Interface ().(Unmarshaler )
if err := unmarshaler .UnmarshalJSON (val ); err != nil {
err = wrapSkipFunc (err , "unmarshal method" )
if uo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
return err
}
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 ()
xd .Flags .Set (jsonflags .WithinArshalCall | 1 )
err := va .Addr ().Interface ().(UnmarshalerFrom ).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 {
err = wrapSkipFunc (err , "unmarshal method" )
if uo .Flags .Get (jsonflags .ReportErrorsWithLegacySemantics ) {
if err2 := xd .SkipUntil (prevDepth , prevLength +1 ); err2 != nil {
return err2
}
return err
}
if !isSyntacticError (err ) && !export .IsIOError (err ) {
err = newSemanticErrorWithPosition (dec , t , prevDepth , prevLength , err )
}
return err
}
return nil
}
}
return fncs
}
func implementsAny(t reflect .Type , ifaceTypes ...reflect .Type ) bool {
for _ , ifaceType := range ifaceTypes {
if _ , ok := implements (t , ifaceType ); ok {
return true
}
}
return false
}
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
}
}
The pages are generated with Golds v0.7.7-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .