|
|
@@ -382,7 +382,12 @@ func isEmptyValue(v reflect.Value) bool {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
|
|
|
|
func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
|
|
|
|
valueEncoder(v)(e, v, opts)
|
|
|
|
tagkey := "json"
|
|
|
|
|
|
|
|
if opts.tagkey != nil {
|
|
|
|
|
|
|
|
tagkey = *opts.tagkey
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
valueEncoder(v, tagkey)(e, v, opts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type encOpts struct {
|
|
|
|
type encOpts struct {
|
|
|
@@ -397,20 +402,22 @@ type encOpts struct {
|
|
|
|
// filter matches jsonfilter tag of struct
|
|
|
|
// filter matches jsonfilter tag of struct
|
|
|
|
// marshals if no jsonfilter is set or otherwise if jsonfilter has the filter value
|
|
|
|
// marshals if no jsonfilter is set or otherwise if jsonfilter has the filter value
|
|
|
|
filter *string
|
|
|
|
filter *string
|
|
|
|
|
|
|
|
// use different tag instead of "json"
|
|
|
|
|
|
|
|
tagkey *string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
|
|
|
|
type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
|
|
|
|
|
|
|
|
|
|
|
|
var encoderCache sync.Map // map[reflect.Type]encoderFunc
|
|
|
|
var encoderCache sync.Map // map[reflect.Type]encoderFunc
|
|
|
|
|
|
|
|
|
|
|
|
func valueEncoder(v reflect.Value) encoderFunc {
|
|
|
|
func valueEncoder(v reflect.Value, tagkey string) encoderFunc {
|
|
|
|
if !v.IsValid() {
|
|
|
|
if !v.IsValid() {
|
|
|
|
return invalidValueEncoder
|
|
|
|
return invalidValueEncoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return typeEncoder(v.Type())
|
|
|
|
return typeEncoder(v.Type(), tagkey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func typeEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func typeEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
if fi, ok := encoderCache.Load(t); ok {
|
|
|
|
if fi, ok := encoderCache.Load(t); ok {
|
|
|
|
return fi.(encoderFunc)
|
|
|
|
return fi.(encoderFunc)
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -433,7 +440,7 @@ func typeEncoder(t reflect.Type) encoderFunc {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Compute the real encoder and replace the indirect func with it.
|
|
|
|
// Compute the real encoder and replace the indirect func with it.
|
|
|
|
f = newTypeEncoder(t, true)
|
|
|
|
f = newTypeEncoder(t, true, tagkey)
|
|
|
|
wg.Done()
|
|
|
|
wg.Done()
|
|
|
|
encoderCache.Store(t, f)
|
|
|
|
encoderCache.Store(t, f)
|
|
|
|
return f
|
|
|
|
return f
|
|
|
@@ -446,19 +453,19 @@ var (
|
|
|
|
|
|
|
|
|
|
|
|
// newTypeEncoder constructs an encoderFunc for a type.
|
|
|
|
// newTypeEncoder constructs an encoderFunc for a type.
|
|
|
|
// The returned encoder only checks CanAddr when allowAddr is true.
|
|
|
|
// The returned encoder only checks CanAddr when allowAddr is true.
|
|
|
|
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
|
|
|
|
func newTypeEncoder(t reflect.Type, allowAddr bool, tagkey string) encoderFunc {
|
|
|
|
// If we have a non-pointer value whose type implements
|
|
|
|
// If we have a non-pointer value whose type implements
|
|
|
|
// Marshaler with a value receiver, then we're better off taking
|
|
|
|
// Marshaler with a value receiver, then we're better off taking
|
|
|
|
// the address of the value - otherwise we end up with an
|
|
|
|
// the address of the value - otherwise we end up with an
|
|
|
|
// allocation as we cast the value to an interface.
|
|
|
|
// allocation as we cast the value to an interface.
|
|
|
|
if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(marshalerType) {
|
|
|
|
if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(marshalerType) {
|
|
|
|
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
|
|
|
|
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false, tagkey))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.Implements(marshalerType) {
|
|
|
|
if t.Implements(marshalerType) {
|
|
|
|
return marshalerEncoder
|
|
|
|
return marshalerEncoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(textMarshalerType) {
|
|
|
|
if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(textMarshalerType) {
|
|
|
|
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
|
|
|
|
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false, tagkey))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.Implements(textMarshalerType) {
|
|
|
|
if t.Implements(textMarshalerType) {
|
|
|
|
return textMarshalerEncoder
|
|
|
|
return textMarshalerEncoder
|
|
|
@@ -480,15 +487,15 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
|
|
|
|
case reflect.Interface:
|
|
|
|
case reflect.Interface:
|
|
|
|
return interfaceEncoder
|
|
|
|
return interfaceEncoder
|
|
|
|
case reflect.Struct:
|
|
|
|
case reflect.Struct:
|
|
|
|
return newStructEncoder(t)
|
|
|
|
return newStructEncoder(t, tagkey)
|
|
|
|
case reflect.Map:
|
|
|
|
case reflect.Map:
|
|
|
|
return newMapEncoder(t)
|
|
|
|
return newMapEncoder(t, tagkey)
|
|
|
|
case reflect.Slice:
|
|
|
|
case reflect.Slice:
|
|
|
|
return newSliceEncoder(t)
|
|
|
|
return newSliceEncoder(t, tagkey)
|
|
|
|
case reflect.Array:
|
|
|
|
case reflect.Array:
|
|
|
|
return newArrayEncoder(t)
|
|
|
|
return newArrayEncoder(t, tagkey)
|
|
|
|
case reflect.Pointer:
|
|
|
|
case reflect.Pointer:
|
|
|
|
return newPtrEncoder(t)
|
|
|
|
return newPtrEncoder(t, tagkey)
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
return unsupportedTypeEncoder
|
|
|
|
return unsupportedTypeEncoder
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -801,8 +808,8 @@ FieldLoop:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newStructEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func newStructEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
se := structEncoder{fields: cachedTypeFields(t)}
|
|
|
|
se := structEncoder{fields: cachedTypeFields(t, tagkey)}
|
|
|
|
return se.encode
|
|
|
|
return se.encode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -855,7 +862,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|
|
|
e.ptrLevel--
|
|
|
|
e.ptrLevel--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newMapEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func newMapEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
switch t.Key().Kind() {
|
|
|
|
switch t.Key().Kind() {
|
|
|
|
case reflect.String,
|
|
|
|
case reflect.String,
|
|
|
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
|
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
|
@@ -865,7 +872,7 @@ func newMapEncoder(t reflect.Type) encoderFunc {
|
|
|
|
return unsupportedTypeEncoder
|
|
|
|
return unsupportedTypeEncoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
me := mapEncoder{typeEncoder(t.Elem())}
|
|
|
|
me := mapEncoder{typeEncoder(t.Elem(), tagkey)}
|
|
|
|
return me.encode
|
|
|
|
return me.encode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -936,7 +943,7 @@ func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|
|
|
e.ptrLevel--
|
|
|
|
e.ptrLevel--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newSliceEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func newSliceEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
// Byte slices get special treatment; arrays don't.
|
|
|
|
// Byte slices get special treatment; arrays don't.
|
|
|
|
if t.Elem().Kind() == reflect.Uint8 {
|
|
|
|
if t.Elem().Kind() == reflect.Uint8 {
|
|
|
|
p := reflect.PointerTo(t.Elem())
|
|
|
|
p := reflect.PointerTo(t.Elem())
|
|
|
@@ -944,7 +951,7 @@ func newSliceEncoder(t reflect.Type) encoderFunc {
|
|
|
|
return encodeByteSlice
|
|
|
|
return encodeByteSlice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enc := sliceEncoder{newArrayEncoder(t)}
|
|
|
|
enc := sliceEncoder{newArrayEncoder(t, tagkey)}
|
|
|
|
return enc.encode
|
|
|
|
return enc.encode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -964,8 +971,8 @@ func (ae arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|
|
|
e.WriteByte(']')
|
|
|
|
e.WriteByte(']')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newArrayEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func newArrayEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
enc := arrayEncoder{typeEncoder(t.Elem())}
|
|
|
|
enc := arrayEncoder{typeEncoder(t.Elem(), tagkey)}
|
|
|
|
return enc.encode
|
|
|
|
return enc.encode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -992,8 +999,8 @@ func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|
|
|
e.ptrLevel--
|
|
|
|
e.ptrLevel--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newPtrEncoder(t reflect.Type) encoderFunc {
|
|
|
|
func newPtrEncoder(t reflect.Type, tagkey string) encoderFunc {
|
|
|
|
enc := ptrEncoder{typeEncoder(t.Elem())}
|
|
|
|
enc := ptrEncoder{typeEncoder(t.Elem(), tagkey)}
|
|
|
|
return enc.encode
|
|
|
|
return enc.encode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -1270,7 +1277,7 @@ func (x byIndex) Less(i, j int) bool {
|
|
|
|
// typeFields returns a list of fields that JSON should recognize for the given type.
|
|
|
|
// typeFields returns a list of fields that JSON should recognize for the given type.
|
|
|
|
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
|
|
|
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
|
|
|
// and then any reachable anonymous structs.
|
|
|
|
// and then any reachable anonymous structs.
|
|
|
|
func typeFields(t reflect.Type) structFields {
|
|
|
|
func typeFields(t reflect.Type, tagkey string) structFields {
|
|
|
|
// Anonymous fields to explore at the current level and the next.
|
|
|
|
// Anonymous fields to explore at the current level and the next.
|
|
|
|
current := []field{}
|
|
|
|
current := []field{}
|
|
|
|
next := []field{{typ: t}}
|
|
|
|
next := []field{{typ: t}}
|
|
|
@@ -1315,7 +1322,7 @@ func typeFields(t reflect.Type) structFields {
|
|
|
|
// Ignore unexported non-embedded fields.
|
|
|
|
// Ignore unexported non-embedded fields.
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tag := sf.Tag.Get("json")
|
|
|
|
tag := sf.Tag.Get(tagkey)
|
|
|
|
if tag == "-" {
|
|
|
|
if tag == "-" {
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -1449,7 +1456,7 @@ func typeFields(t reflect.Type) structFields {
|
|
|
|
|
|
|
|
|
|
|
|
for i := range fields {
|
|
|
|
for i := range fields {
|
|
|
|
f := &fields[i]
|
|
|
|
f := &fields[i]
|
|
|
|
f.encoder = typeEncoder(typeByIndex(t, f.index))
|
|
|
|
f.encoder = typeEncoder(typeByIndex(t, f.index), tagkey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nameIndex := make(map[string]int, len(fields))
|
|
|
|
nameIndex := make(map[string]int, len(fields))
|
|
|
|
for i, field := range fields {
|
|
|
|
for i, field := range fields {
|
|
|
@@ -1474,13 +1481,26 @@ func dominantField(fields []field) (field, bool) {
|
|
|
|
return fields[0], true
|
|
|
|
return fields[0], true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var fieldCache sync.Map // map[reflect.Type]structFields
|
|
|
|
var fieldCache sync.Map // map[string]map[reflect.Type]structFields
|
|
|
|
|
|
|
|
|
|
|
|
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
|
|
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
|
|
func cachedTypeFields(t reflect.Type) structFields {
|
|
|
|
func cachedTypeFields(t reflect.Type, tagkey string) structFields {
|
|
|
|
if f, ok := fieldCache.Load(t); ok {
|
|
|
|
if m0, ok := fieldCache.Load(tagkey); ok {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if f, ok := m0.(*sync.Map).Load(t); ok {
|
|
|
|
|
|
|
|
return f.(structFields)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
f, _ := m0.(*sync.Map).LoadOrStore(t, typeFields(t, tagkey))
|
|
|
|
|
|
|
|
return f.(structFields)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m0 := &sync.Map{}
|
|
|
|
|
|
|
|
f, _ := m0.LoadOrStore(t, typeFields(t, tagkey))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fieldCache.Store(tagkey, m0)
|
|
|
|
|
|
|
|
|
|
|
|
return f.(structFields)
|
|
|
|
return f.(structFields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f, _ := fieldCache.LoadOrStore(t, typeFields(t))
|
|
|
|
|
|
|
|
return f.(structFields)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|