package json
import (
"cmp"
"reflect"
"strconv"
"encoding/json/internal"
"encoding/json/internal/jsonflags"
"encoding/json/internal/jsonopts"
"encoding/json/internal/jsonwire"
"encoding/json/jsontext"
)
func marshalValueAny(enc *jsontext .Encoder , val any , mo *jsonopts .Struct ) error {
switch val := val .(type ) {
case nil :
return enc .WriteToken (jsontext .Null )
case bool :
return enc .WriteToken (jsontext .Bool (val ))
case string :
return enc .WriteToken (jsontext .String (val ))
case float64 :
return enc .WriteToken (jsontext .Float (val ))
case map [string ]any :
return marshalObjectAny (enc , val , mo )
case []any :
return marshalArrayAny (enc , val , mo )
default :
v := newAddressableValue (reflect .TypeOf (val ))
v .Set (reflect .ValueOf (val ))
marshal := lookupArshaler (v .Type ()).marshal
if mo .Marshalers != nil {
marshal , _ = mo .Marshalers .(*Marshalers ).lookup (marshal , v .Type ())
}
return marshal (enc , v , mo )
}
}
func unmarshalValueAny(dec *jsontext .Decoder , uo *jsonopts .Struct ) (any , error ) {
switch k := dec .PeekKind (); k {
case '{' :
return unmarshalObjectAny (dec , uo )
case '[' :
return unmarshalArrayAny (dec , uo )
default :
xd := export .Decoder (dec )
var flags jsonwire .ValueFlags
val , err := xd .ReadValue (&flags )
if err != nil {
return nil , err
}
switch val .Kind () {
case 'n' :
return nil , nil
case 'f' :
return false , nil
case 't' :
return true , nil
case '"' :
val = jsonwire .UnquoteMayCopy (val , flags .IsVerbatim ())
if xd .StringCache == nil {
xd .StringCache = new (stringCache )
}
return makeString (xd .StringCache , val ), nil
case '0' :
if uo .Flags .Get (jsonflags .UnmarshalAnyWithRawNumber ) {
return internal .RawNumberOf (val ), nil
}
fv , ok := jsonwire .ParseFloat (val , 64 )
if !ok {
return fv , newUnmarshalErrorAfterWithValue (dec , float64Type , strconv .ErrRange )
}
return fv , nil
default :
panic ("BUG: invalid kind: " + k .String ())
}
}
}
func marshalObjectAny(enc *jsontext .Encoder , obj map [string ]any , mo *jsonopts .Struct ) error {
xe := export .Encoder (enc )
if xe .Tokens .Depth () > startDetectingCyclesAfter {
v := reflect .ValueOf (obj )
if err := visitPointer (&xe .SeenPointers , v ); err != nil {
return newMarshalErrorBefore (enc , anyType , err )
}
defer leavePointer (&xe .SeenPointers , v )
}
if len (obj ) == 0 {
if mo .Flags .Get (jsonflags .FormatNilMapAsNull ) && obj == nil {
return enc .WriteToken (jsontext .Null )
}
if !mo .Flags .Get (jsonflags .AnyWhitespace ) && !xe .Tokens .Last .NeedObjectName () {
xe .Buf = append (xe .Tokens .MayAppendDelim (xe .Buf , '{' ), "{}" ...)
xe .Tokens .Last .Increment ()
if xe .NeedFlush () {
return xe .Flush ()
}
return nil
}
}
if err := enc .WriteToken (jsontext .BeginObject ); err != nil {
return err
}
if !mo .Flags .Get (jsonflags .AllowInvalidUTF8 ) {
xe .Tokens .Last .DisableNamespace ()
}
if !mo .Flags .Get (jsonflags .Deterministic ) || len (obj ) <= 1 {
for name , val := range obj {
if err := enc .WriteToken (jsontext .String (name )); err != nil {
return err
}
if err := marshalValueAny (enc , val , mo ); err != nil {
return err
}
}
} else {
names := getStrings (len (obj ))
var i int
for name := range obj {
(*names )[i ] = name
i ++
}
names .Sort ()
for _ , name := range *names {
if err := enc .WriteToken (jsontext .String (name )); err != nil {
return err
}
if err := marshalValueAny (enc , obj [name ], mo ); err != nil {
return err
}
}
putStrings (names )
}
if err := enc .WriteToken (jsontext .EndObject ); err != nil {
return err
}
return nil
}
func unmarshalObjectAny(dec *jsontext .Decoder , uo *jsonopts .Struct ) (map [string ]any , error ) {
switch tok , err := dec .ReadToken (); {
case err != nil :
return nil , err
case tok .Kind () != '{' :
panic ("BUG: invalid kind: " + tok .Kind ().String ())
}
obj := make (map [string ]any )
if !uo .Flags .Get (jsonflags .AllowInvalidUTF8 ) {
export .Decoder (dec ).Tokens .Last .DisableNamespace ()
}
var errUnmarshal error
for dec .PeekKind () != '}' {
tok , err := dec .ReadToken ()
if err != nil {
return obj , err
}
name := tok .String ()
if _ , ok := obj [name ]; ok {
name := export .Decoder (dec ).PreviousTokenOrValue ()
err := newDuplicateNameError (dec .StackPointer (), nil , dec .InputOffset ()-len64 (name ))
return obj , err
}
val , err := unmarshalValueAny (dec , uo )
obj [name ] = val
if err != nil {
if isFatalError (err , uo .Flags ) {
return obj , err
}
errUnmarshal = cmp .Or (err , errUnmarshal )
}
}
if _ , err := dec .ReadToken (); err != nil {
return obj , err
}
return obj , errUnmarshal
}
func marshalArrayAny(enc *jsontext .Encoder , arr []any , mo *jsonopts .Struct ) error {
xe := export .Encoder (enc )
if xe .Tokens .Depth () > startDetectingCyclesAfter {
v := reflect .ValueOf (arr )
if err := visitPointer (&xe .SeenPointers , v ); err != nil {
return newMarshalErrorBefore (enc , sliceAnyType , err )
}
defer leavePointer (&xe .SeenPointers , v )
}
if len (arr ) == 0 {
if mo .Flags .Get (jsonflags .FormatNilSliceAsNull ) && arr == nil {
return enc .WriteToken (jsontext .Null )
}
if !mo .Flags .Get (jsonflags .AnyWhitespace ) && !xe .Tokens .Last .NeedObjectName () {
xe .Buf = append (xe .Tokens .MayAppendDelim (xe .Buf , '[' ), "[]" ...)
xe .Tokens .Last .Increment ()
if xe .NeedFlush () {
return xe .Flush ()
}
return nil
}
}
if err := enc .WriteToken (jsontext .BeginArray ); err != nil {
return err
}
for _ , val := range arr {
if err := marshalValueAny (enc , val , mo ); err != nil {
return err
}
}
if err := enc .WriteToken (jsontext .EndArray ); err != nil {
return err
}
return nil
}
func unmarshalArrayAny(dec *jsontext .Decoder , uo *jsonopts .Struct ) ([]any , error ) {
switch tok , err := dec .ReadToken (); {
case err != nil :
return nil , err
case tok .Kind () != '[' :
panic ("BUG: invalid kind: " + tok .Kind ().String ())
}
arr := []any {}
var errUnmarshal error
for dec .PeekKind () != ']' {
val , err := unmarshalValueAny (dec , uo )
arr = append (arr , val )
if err != nil {
if isFatalError (err , uo .Flags ) {
return arr , err
}
errUnmarshal = cmp .Or (errUnmarshal , err )
}
}
if _ , err := dec .ReadToken (); err != nil {
return arr , err
}
return arr , errUnmarshal
}
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 .