From dcd106c1cdbf1d26d9836a29ab794f441303acca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Mon, 18 Mar 2024 10:42:00 +0100 Subject: [PATCH] v0.0.415 add 'tagkey' to gojson.Decoder --- goextVersion.go | 4 +-- gojson/decode.go | 7 ++++- gojson/encode.go | 82 ++++++++++++++++++++++++++++++------------------ gojson/stream.go | 3 ++ 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/goextVersion.go b/goextVersion.go index 67fe519..44071ba 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -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" diff --git a/gojson/decode.go b/gojson/decode.go index 01af489..177658e 100644 --- a/gojson/decode.go +++ b/gojson/decode.go @@ -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)}) diff --git a/gojson/encode.go b/gojson/encode.go index af7fab9..aca278f 100644 --- a/gojson/encode.go +++ b/gojson/encode.go @@ -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) + } diff --git a/gojson/stream.go b/gojson/stream.go index b278ee4..8e09814 100644 --- a/gojson/stream.go +++ b/gojson/stream.go @@ -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. //