1
0
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:
Tao Wen 2018-02-16 17:32:41 +08:00
parent a7a34507ab
commit 9dafbc667f
6 changed files with 66 additions and 50 deletions

View File

@ -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:

View File

@ -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,
} }

View File

@ -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

View File

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

View File

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

View File

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