v0.0.415 add 'tagkey' to gojson.Decoder
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 3m45s
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 3m45s
This commit is contained in:
parent
b704e2a362
commit
dcd106c1cd
@ -1,5 +1,5 @@
|
|||||||
package goext
|
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"
|
||||||
|
@ -217,6 +217,7 @@ type decodeState struct {
|
|||||||
savedError error
|
savedError error
|
||||||
useNumber bool
|
useNumber bool
|
||||||
disallowUnknownFields bool
|
disallowUnknownFields bool
|
||||||
|
tagkey *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// readIndex returns the position of the last byte read.
|
// 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))
|
v.Set(reflect.MakeMap(t))
|
||||||
}
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
fields = cachedTypeFields(t)
|
tagkey := "json"
|
||||||
|
if d.tagkey != nil {
|
||||||
|
tagkey = *d.tagkey
|
||||||
|
}
|
||||||
|
fields = cachedTypeFields(t, tagkey)
|
||||||
// ok
|
// ok
|
||||||
default:
|
default:
|
||||||
d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
|
d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)})
|
||||||
|
@ -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)
|
return f.(structFields)
|
||||||
}
|
}
|
||||||
f, _ := fieldCache.LoadOrStore(t, typeFields(t))
|
f, _ := m0.(*sync.Map).LoadOrStore(t, typeFields(t, tagkey))
|
||||||
return f.(structFields)
|
return f.(structFields)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
m0 := &sync.Map{}
|
||||||
|
f, _ := m0.LoadOrStore(t, typeFields(t, tagkey))
|
||||||
|
|
||||||
|
fieldCache.Store(tagkey, m0)
|
||||||
|
|
||||||
|
return f.(structFields)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,9 @@ func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
|
|||||||
// non-ignored, exported fields in the destination.
|
// non-ignored, exported fields in the destination.
|
||||||
func (dec *Decoder) DisallowUnknownFields() { dec.d.disallowUnknownFields = true }
|
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
|
// Decode reads the next JSON-encoded value from its
|
||||||
// input and stores it in the value pointed to by v.
|
// input and stores it in the value pointed to by v.
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user