1
0
mirror of https://github.com/json-iterator/go.git synced 2025-03-26 21:12:40 +02:00

fix the case when embedded struct ptr is nil

This commit is contained in:
Tao Wen 2017-06-29 18:45:11 +08:00
parent 4e608af2c7
commit 84fa033353
4 changed files with 104 additions and 47 deletions

@ -78,17 +78,37 @@ func (decoder *optionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*unsafe.Pointer)(ptr)) = nil
} else {
if *((*unsafe.Pointer)(ptr)) == nil {
// pointer to null, we have to allocate memory to hold the value
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.valueType)
decoder.valueDecoder.Decode(unsafe.Pointer(value.Pointer()), iter)
*((*uintptr)(ptr)) = value.Pointer()
newPtr := extractInterface(value.Interface()).word
decoder.valueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
} else {
// reuse existing instance
//reuse existing instance
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
}
}
}
type deferenceDecoder struct {
// only to deference a pointer
valueType reflect.Type
valueDecoder ValDecoder
}
func (decoder *deferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if *((*unsafe.Pointer)(ptr)) == nil {
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.valueType)
newPtr := extractInterface(value.Interface()).word
decoder.valueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
} else {
//reuse existing instance
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
}
}
type optionalEncoder struct {
valueEncoder ValEncoder
}

@ -15,8 +15,10 @@ var fieldEncoders = map[string]ValEncoder{}
var extensions = []Extension{}
type StructDescriptor struct {
Type reflect.Type
Fields []*Binding
onePtrEmbedded bool
onePtrOptimization bool
Type reflect.Type
Fields []*Binding
}
func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding {
@ -219,10 +221,12 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
return nil, err
}
for _, binding := range structDescriptor.Fields {
cloneField := field
cloneField.Name = binding.Field.Name
binding.Encoder = &optionalEncoder{binding.Encoder}
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, false}
binding.Decoder = &optionalDecoder{field.Type, binding.Decoder}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
binding.Encoder = &structFieldEncoder{&cloneField, binding.Encoder, false}
binding.Decoder = &deferenceDecoder{field.Type.Elem(), binding.Decoder}
binding.Decoder = &structFieldDecoder{&cloneField, binding.Decoder}
if field.Offset == 0 {
headAnonymousBindings = append(headAnonymousBindings, binding)
} else {
@ -267,9 +271,27 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
return createStructDescriptor(cfg, typ, bindings, headAnonymousBindings, tailAnonymousBindings), nil
}
func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, headAnonymousBindings []*Binding, tailAnonymousBindings []*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:
fallthrough
case reflect.Slice:
onePtrOptimization = true
}
}
structDescriptor := &StructDescriptor{
Type: typ,
Fields: bindings,
onePtrEmbedded: onePtrEmbedded,
onePtrOptimization: onePtrOptimization,
Type: typ,
Fields: bindings,
}
for _, extension := range extensions {
extension.UpdateStructDescriptor(structDescriptor)

@ -36,7 +36,7 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
finalOrderedFields = append(finalOrderedFields, *structFieldTo)
}
}
return &structEncoder{finalOrderedFields}, nil
return &structEncoder{structDescriptor.onePtrEmbedded,structDescriptor.onePtrOptimization,finalOrderedFields}, nil
}
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
@ -992,6 +992,8 @@ func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
}
type structEncoder struct {
onePtrEmbedded bool
onePtrOptimization bool
fields []structFieldTo
}
@ -1018,44 +1020,21 @@ func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
}
func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {
var encoderToUse ValEncoder
encoderToUse = encoder
if len(encoder.fields) == 1 {
firstFieldName := encoder.fields[0].toName
firstField := encoder.fields[0].encoder
firstEncoder := firstField.fieldEncoder
firstEncoderName := reflect.TypeOf(firstEncoder).String()
// interface{} has inline optimization for this case
if firstEncoderName == "*jsoniter.optionalEncoder" {
encoderToUse = &structEncoder{
fields: []structFieldTo{
{
toName: firstFieldName,
encoder: &structFieldEncoder{
field: firstField.field,
fieldEncoder: firstEncoder.(*optionalEncoder).valueEncoder,
omitempty: firstField.omitempty,
},
},
},
}
e := (*emptyInterface)(unsafe.Pointer(&val))
if e.word == nil {
stream.WriteObjectStart()
stream.WriteObjectField(firstFieldName)
stream.WriteNil()
stream.WriteObjectEnd()
return
}
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoderToUse.Encode(unsafe.Pointer(&e.word), stream)
} else {
encoderToUse.Encode(e.word, stream)
}
e := (*emptyInterface)(unsafe.Pointer(&val))
if encoder.onePtrOptimization {
if e.word == nil && encoder.onePtrEmbedded {
stream.WriteObjectStart()
stream.WriteObjectEnd()
return
}
ptr := uintptr(e.word)
e.word = unsafe.Pointer(&ptr)
}
if reflect.TypeOf(val).Kind() == reflect.Ptr {
encoder.Encode(unsafe.Pointer(&e.word), stream)
} else {
encoder.Encode(e.word, stream)
}
WriteToStream(val, stream, encoderToUse)
}
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {

@ -197,6 +197,42 @@ func Test_struct_with_one_nil(t *testing.T) {
should.Equal(`{"F":null}`, output)
}
func Test_struct_with_one_nil_embedded(t *testing.T) {
type Parent struct {
Field1 string
Field2 string
}
type TestObject struct {
*Parent
}
obj := TestObject{}
should := require.New(t)
bytes, err := json.Marshal(obj)
should.Nil(err)
should.Equal("{}", string(bytes))
output, err := MarshalToString(obj)
should.Nil(err)
should.Equal(`{}`, output)
}
func Test_struct_with_not_nil_embedded(t *testing.T) {
type Parent struct {
Field0 string
Field1 []string
Field2 map[string]interface{}
}
type TestObject struct {
*Parent
}
should := require.New(t)
var obj TestObject
err := UnmarshalFromString(`{"Field0":"1","Field1":null,"Field2":{"K":"V"}}`, &obj)
should.Nil(err)
should.Nil(obj.Field1)
should.Equal(map[string]interface{}{"K": "V"}, obj.Field2)
should.Equal("1", obj.Field0)
}
func Test_array_with_one_nil(t *testing.T) {
obj := [1]*float64{nil}
should := require.New(t)