mirror of
https://github.com/json-iterator/go.git
synced 2025-06-03 22:27:26 +02:00
when embedded ptr is nil, the fields should be omitted
This commit is contained in:
parent
a7a34507ab
commit
9dafbc667f
@ -340,14 +340,10 @@ func createEncoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
|
|||||||
}
|
}
|
||||||
if typ.Implements(textMarshalerType) {
|
if typ.Implements(textMarshalerType) {
|
||||||
checkIsEmpty := createCheckIsEmpty(cfg, typ)
|
checkIsEmpty := createCheckIsEmpty(cfg, typ)
|
||||||
templateInterface := reflect.New(typ).Elem().Interface()
|
|
||||||
var encoder ValEncoder = &textMarshalerEncoder{
|
var encoder ValEncoder = &textMarshalerEncoder{
|
||||||
templateInterface: extractInterface(templateInterface),
|
valType: reflect2.Type2(typ),
|
||||||
checkIsEmpty: checkIsEmpty,
|
checkIsEmpty: checkIsEmpty,
|
||||||
}
|
}
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
encoder = &OptionalEncoder{encoder}
|
|
||||||
}
|
|
||||||
return encoder
|
return encoder
|
||||||
}
|
}
|
||||||
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
|
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
|
||||||
@ -393,10 +389,7 @@ func createCheckIsEmpty(cfg *frozenConfig, typ reflect.Type) checkIsEmpty {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return &boolCodec{}
|
return &boolCodec{}
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
return &dynamicEncoder{reflect2.Type2(typ)}
|
||||||
return &emptyInterfaceCodec{}
|
|
||||||
}
|
|
||||||
return &nonEmptyInterfaceCodec{}
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return &structEncoder{typ: typ}
|
return &structEncoder{typ: typ}
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
@ -492,10 +485,7 @@ func createEncoderOfSimpleType(cfg *frozenConfig, prefix string, typ reflect.Typ
|
|||||||
}
|
}
|
||||||
return &boolCodec{}
|
return &boolCodec{}
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
return &dynamicEncoder{reflect2.Type2(typ)}
|
||||||
return &emptyInterfaceCodec{}
|
|
||||||
}
|
|
||||||
return &nonEmptyInterfaceCodec{}
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return encoderOfStruct(cfg, prefix, typ)
|
return encoderOfStruct(cfg, prefix, typ)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
|
@ -17,8 +17,6 @@ var extensions = []Extension{}
|
|||||||
|
|
||||||
// StructDescriptor describe how should we encode/decode the struct
|
// StructDescriptor describe how should we encode/decode the struct
|
||||||
type StructDescriptor struct {
|
type StructDescriptor struct {
|
||||||
onePtrEmbedded bool
|
|
||||||
onePtrOptimization bool
|
|
||||||
Type reflect.Type
|
Type reflect.Type
|
||||||
Fields []*Binding
|
Fields []*Binding
|
||||||
}
|
}
|
||||||
@ -297,25 +295,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
|
|||||||
return createStructDescriptor(cfg, typ, bindings, embeddedBindings)
|
return createStructDescriptor(cfg, typ, bindings, embeddedBindings)
|
||||||
}
|
}
|
||||||
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
|
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
|
||||||
onePtrEmbedded := false
|
|
||||||
onePtrOptimization := false
|
|
||||||
if typ.NumField() == 1 {
|
|
||||||
firstField := typ.Field(0)
|
|
||||||
switch firstField.Type.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if firstField.Anonymous && firstField.Type.Elem().Kind() == reflect.Struct {
|
|
||||||
onePtrEmbedded = true
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case reflect.Map:
|
|
||||||
onePtrOptimization = true
|
|
||||||
case reflect.Struct:
|
|
||||||
onePtrOptimization = isStructOnePtr(firstField.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
structDescriptor := &StructDescriptor{
|
structDescriptor := &StructDescriptor{
|
||||||
onePtrEmbedded: onePtrEmbedded,
|
|
||||||
onePtrOptimization: onePtrOptimization,
|
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Fields: bindings,
|
Fields: bindings,
|
||||||
}
|
}
|
||||||
|
@ -357,6 +357,20 @@ func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return nonEmptyInterface.word == nil
|
return nonEmptyInterface.word == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dynamicEncoder struct {
|
||||||
|
valType reflect2.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
obj := encoder.valType.UnsafeIndirect(ptr)
|
||||||
|
stream.WriteVal(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
|
return encoder.valType.UnsafeIndirect(ptr) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type anyCodec struct {
|
type anyCodec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +404,7 @@ func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
number := *((*json.Number)(ptr))
|
number := *((*json.Number)(ptr))
|
||||||
if len(number) == 0 {
|
if len(number) == 0 {
|
||||||
stream.WriteRaw("0")
|
stream.writeByte('0')
|
||||||
} else {
|
} else {
|
||||||
stream.WriteRaw(string(number))
|
stream.WriteRaw(string(number))
|
||||||
}
|
}
|
||||||
@ -418,7 +432,7 @@ func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
number := *((*Number)(ptr))
|
number := *((*Number)(ptr))
|
||||||
if len(number) == 0 {
|
if len(number) == 0 {
|
||||||
stream.WriteRaw("0")
|
stream.writeByte('0')
|
||||||
} else {
|
} else {
|
||||||
stream.WriteRaw(string(number))
|
stream.WriteRaw(string(number))
|
||||||
}
|
}
|
||||||
@ -603,15 +617,17 @@ func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type textMarshalerEncoder struct {
|
type textMarshalerEncoder struct {
|
||||||
templateInterface emptyInterface
|
valType reflect2.Type
|
||||||
checkIsEmpty checkIsEmpty
|
checkIsEmpty checkIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
templateInterface := encoder.templateInterface
|
obj := encoder.valType.UnsafeIndirect(ptr)
|
||||||
templateInterface.word = ptr
|
if obj == nil {
|
||||||
realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
|
stream.WriteNil()
|
||||||
marshaler := (*realInterface).(encoding.TextMarshaler)
|
return
|
||||||
|
}
|
||||||
|
marshaler := (obj).(encoding.TextMarshaler)
|
||||||
bytes, err := marshaler.MarshalText()
|
bytes, err := marshaler.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stream.Error = err
|
stream.Error = err
|
||||||
|
@ -88,5 +88,22 @@ func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
return encoder.ValueEncoder.IsEmpty(*((*unsafe.Pointer)(ptr)))
|
dePtr := *((*unsafe.Pointer)(ptr))
|
||||||
|
if dePtr == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return encoder.ValueEncoder.IsEmpty(dePtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
||||||
|
deReferenced := *((*unsafe.Pointer)(ptr))
|
||||||
|
if deReferenced == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil)
|
||||||
|
if !converted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fieldPtr := unsafe.Pointer(deReferenced)
|
||||||
|
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
||||||
}
|
}
|
@ -42,8 +42,7 @@ func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEnco
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &structEncoder{typ, structDescriptor.onePtrEmbedded,
|
return &structEncoder{typ, finalOrderedFields}
|
||||||
structDescriptor.onePtrOptimization, finalOrderedFields}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
|
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
|
||||||
@ -120,11 +119,22 @@ func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
||||||
|
isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
|
||||||
|
if !converted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
|
||||||
|
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsEmbeddedPtrNil interface {
|
||||||
|
IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
|
||||||
|
}
|
||||||
|
|
||||||
type structEncoder struct {
|
type structEncoder struct {
|
||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
onePtrEmbedded bool
|
fields []structFieldTo
|
||||||
onePtrOptimization bool
|
|
||||||
fields []structFieldTo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type structFieldTo struct {
|
type structFieldTo struct {
|
||||||
@ -139,6 +149,9 @@ func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if field.encoder.IsEmbeddedPtrNil(ptr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if isNotFirst {
|
if isNotFirst {
|
||||||
stream.WriteMore()
|
stream.WriteMore()
|
||||||
}
|
}
|
@ -92,7 +92,8 @@ func init() {
|
|||||||
}{&StructVarious{}},
|
}{&StructVarious{}},
|
||||||
struct {
|
struct {
|
||||||
*StructVarious
|
*StructVarious
|
||||||
}{},
|
Field int
|
||||||
|
}{nil, 10},
|
||||||
struct {
|
struct {
|
||||||
Field1 int
|
Field1 int
|
||||||
Field2 [1]*float64
|
Field2 [1]*float64
|
||||||
@ -172,7 +173,6 @@ type CacheItem struct {
|
|||||||
MaxAge int `json:"cacheAge"`
|
MaxAge int `json:"cacheAge"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type orderA struct {
|
type orderA struct {
|
||||||
Field2 string
|
Field2 string
|
||||||
}
|
}
|
||||||
@ -193,4 +193,4 @@ type structOrder struct {
|
|||||||
Field3 string
|
Field3 string
|
||||||
orderB
|
orderB
|
||||||
Field7 string
|
Field7 string
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user