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:
Mike Schwörer 2024-03-18 10:42:00 +01:00
parent b704e2a362
commit dcd106c1cd
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
4 changed files with 62 additions and 34 deletions

View File

@ -1,5 +1,5 @@
package goext
const GoextVersion = "0.0.414"
const GoextVersion = "0.0.415"
const GoextVersionTimestamp = "2024-03-16T19:42:59+0100"
const GoextVersionTimestamp = "2024-03-18T10:42:00+0100"

View File

@ -217,6 +217,7 @@ type decodeState struct {
savedError error
useNumber bool
disallowUnknownFields bool
tagkey *string
}
// readIndex returns the position of the last byte read.
@ -652,7 +653,11 @@ func (d *decodeState) object(v reflect.Value) error {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
fields = cachedTypeFields(t)
tagkey := "json"
if d.tagkey != nil {
tagkey = *d.tagkey
}
fields = cachedTypeFields(t, tagkey)
// ok
default:
d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})

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)
}

View File

@ -41,6 +41,9 @@ func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
// non-ignored, exported fields in the destination.
func (dec *Decoder) DisallowUnknownFields() { dec.d.disallowUnknownFields = true }
// TagKey sets a different TagKey (instead of "json")
func (dec *Decoder) TagKey(v string) { dec.d.tagkey = &v }
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//