v0.0.415 add 'tagkey' to gojson.Decoder
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 3m45s

This commit is contained in:
2024-03-18 10:42:00 +01:00
parent b704e2a362
commit dcd106c1cd
4 changed files with 62 additions and 34 deletions

View File

@@ -382,7 +382,12 @@ func isEmptyValue(v reflect.Value) bool {
}
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 {
@@ -397,20 +402,22 @@ type encOpts struct {
// filter matches jsonfilter tag of struct
// marshals if no jsonfilter is set or otherwise if jsonfilter has the filter value
filter *string
// use different tag instead of "json"
tagkey *string
}
type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
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() {
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 {
return fi.(encoderFunc)
}
@@ -433,7 +440,7 @@ func typeEncoder(t reflect.Type) encoderFunc {
}
// Compute the real encoder and replace the indirect func with it.
f = newTypeEncoder(t, true)
f = newTypeEncoder(t, true, tagkey)
wg.Done()
encoderCache.Store(t, f)
return f
@@ -446,19 +453,19 @@ var (
// newTypeEncoder constructs an encoderFunc for a type.
// 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
// Marshaler with a value receiver, then we're better off taking
// the address of the value - otherwise we end up with an
// allocation as we cast the value to an interface.
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) {
return marshalerEncoder
}
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) {
return textMarshalerEncoder
@@ -480,15 +487,15 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
case reflect.Interface:
return interfaceEncoder
case reflect.Struct:
return newStructEncoder(t)
return newStructEncoder(t, tagkey)
case reflect.Map:
return newMapEncoder(t)
return newMapEncoder(t, tagkey)
case reflect.Slice:
return newSliceEncoder(t)
return newSliceEncoder(t, tagkey)
case reflect.Array:
return newArrayEncoder(t)
return newArrayEncoder(t, tagkey)
case reflect.Pointer:
return newPtrEncoder(t)
return newPtrEncoder(t, tagkey)
default:
return unsupportedTypeEncoder
}
@@ -801,8 +808,8 @@ FieldLoop:
}
}
func newStructEncoder(t reflect.Type) encoderFunc {
se := structEncoder{fields: cachedTypeFields(t)}
func newStructEncoder(t reflect.Type, tagkey string) encoderFunc {
se := structEncoder{fields: cachedTypeFields(t, tagkey)}
return se.encode
}
@@ -855,7 +862,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.ptrLevel--
}
func newMapEncoder(t reflect.Type) encoderFunc {
func newMapEncoder(t reflect.Type, tagkey string) encoderFunc {
switch t.Key().Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
@@ -865,7 +872,7 @@ func newMapEncoder(t reflect.Type) encoderFunc {
return unsupportedTypeEncoder
}
}
me := mapEncoder{typeEncoder(t.Elem())}
me := mapEncoder{typeEncoder(t.Elem(), tagkey)}
return me.encode
}
@@ -936,7 +943,7 @@ func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.ptrLevel--
}
func newSliceEncoder(t reflect.Type) encoderFunc {
func newSliceEncoder(t reflect.Type, tagkey string) encoderFunc {
// Byte slices get special treatment; arrays don't.
if t.Elem().Kind() == reflect.Uint8 {
p := reflect.PointerTo(t.Elem())
@@ -944,7 +951,7 @@ func newSliceEncoder(t reflect.Type) encoderFunc {
return encodeByteSlice
}
}
enc := sliceEncoder{newArrayEncoder(t)}
enc := sliceEncoder{newArrayEncoder(t, tagkey)}
return enc.encode
}
@@ -964,8 +971,8 @@ func (ae arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte(']')
}
func newArrayEncoder(t reflect.Type) encoderFunc {
enc := arrayEncoder{typeEncoder(t.Elem())}
func newArrayEncoder(t reflect.Type, tagkey string) encoderFunc {
enc := arrayEncoder{typeEncoder(t.Elem(), tagkey)}
return enc.encode
}
@@ -992,8 +999,8 @@ func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.ptrLevel--
}
func newPtrEncoder(t reflect.Type) encoderFunc {
enc := ptrEncoder{typeEncoder(t.Elem())}
func newPtrEncoder(t reflect.Type, tagkey string) encoderFunc {
enc := ptrEncoder{typeEncoder(t.Elem(), tagkey)}
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.
// The algorithm is breadth-first search over the set of structs to include - the top struct
// 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.
current := []field{}
next := []field{{typ: t}}
@@ -1315,7 +1322,7 @@ func typeFields(t reflect.Type) structFields {
// Ignore unexported non-embedded fields.
continue
}
tag := sf.Tag.Get("json")
tag := sf.Tag.Get(tagkey)
if tag == "-" {
continue
}
@@ -1449,7 +1456,7 @@ func typeFields(t reflect.Type) structFields {
for i := range fields {
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))
for i, field := range fields {
@@ -1474,13 +1481,26 @@ func dominantField(fields []field) (field, bool) {
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.
func cachedTypeFields(t reflect.Type) structFields {
if f, ok := fieldCache.Load(t); ok {
func cachedTypeFields(t reflect.Type, tagkey string) structFields {
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)
}
f, _ := fieldCache.LoadOrStore(t, typeFields(t))
return f.(structFields)
}