diff --git a/feature_reflect.go b/feature_reflect.go index 3c5f07c..38b1a30 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -181,7 +181,6 @@ func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { } type optionalEncoder struct { - valueType reflect.Type valueEncoder Encoder } @@ -205,6 +204,30 @@ func (encoder *optionalEncoder) isEmpty(ptr unsafe.Pointer) bool { } } +type placeholderEncoder struct { + valueEncoder Encoder +} + +func (encoder *placeholderEncoder) encode(ptr unsafe.Pointer, stream *Stream) { + encoder.valueEncoder.encode(ptr, stream) +} + +func (encoder *placeholderEncoder) encodeInterface(val interface{}, stream *Stream) { + WriteToStream(val, stream, encoder) +} + +func (encoder *placeholderEncoder) isEmpty(ptr unsafe.Pointer) bool { + return encoder.valueEncoder.isEmpty(ptr) +} + +type placeholderDecoder struct { + valueDecoder Decoder +} + +func (decoder *placeholderDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.valueDecoder.decode(ptr, iter) +} + type mapDecoder struct { mapType reflect.Type elemType reflect.Type @@ -374,6 +397,20 @@ func decoderOfType(typ reflect.Type) (Decoder, error) { if typeDecoder != nil { return typeDecoder, nil } + cacheKey := typ + cachedDecoder := getDecoderFromCache(cacheKey) + if cachedDecoder != nil { + return cachedDecoder, nil + } + placeholder := &placeholderDecoder{} + addDecoderToCache(cacheKey, placeholder) + newDecoder, err := createDecoderOfType(typ) + placeholder.valueDecoder = newDecoder + addDecoderToCache(cacheKey, newDecoder) + return newDecoder, err +} + +func createDecoderOfType(typ reflect.Type) (Decoder, error) { switch typ.Kind() { case reflect.String: return &stringCodec{}, nil @@ -410,7 +447,7 @@ func decoderOfType(typ reflect.Type) (Decoder, error) { return nil, errors.New("unsupportd type: " + typ.String()) } case reflect.Struct: - return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(typ)) + return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ)) case reflect.Slice: return prefix("[slice]").addToDecoder(decoderOfSlice(typ)) case reflect.Map: @@ -431,6 +468,20 @@ func encoderOfType(typ reflect.Type) (Encoder, error) { if typeEncoder != nil { return typeEncoder, nil } + cacheKey := typ + cachedEncoder := getEncoderFromCache(cacheKey) + if cachedEncoder != nil { + return cachedEncoder, nil + } + placeholder := &placeholderEncoder{} + addEncoderToCache(cacheKey, placeholder) + newEncoder, err := createEncoderOfType(typ) + placeholder.valueEncoder = newEncoder + addEncoderToCache(cacheKey, newEncoder) + return newEncoder, err +} + +func createEncoderOfType(typ reflect.Type) (Encoder, error) { switch typ.Kind() { case reflect.String: return &stringCodec{}, nil @@ -463,7 +514,7 @@ func encoderOfType(typ reflect.Type) (Encoder, error) { case reflect.Interface: return &interfaceCodec{}, nil case reflect.Struct: - return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(typ)) + return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ)) case reflect.Slice: return prefix("[slice]").addToEncoder(encoderOfSlice(typ)) case reflect.Map: @@ -490,7 +541,7 @@ func encoderOfOptional(typ reflect.Type) (Encoder, error) { if err != nil { return nil, err } - return &optionalEncoder{elemType, decoder}, nil + return &optionalEncoder{ decoder}, nil } func decoderOfMap(typ reflect.Type) (Decoder, error) { diff --git a/feature_reflect_array.go b/feature_reflect_array.go index 000c3e8..944a0a0 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -21,7 +21,7 @@ func encoderOfSlice(typ reflect.Type) (Encoder, error) { return nil, err } if typ.Elem().Kind() == reflect.Map { - encoder = &optionalEncoder{typ.Elem(), encoder} + encoder = &optionalEncoder{ encoder} } return &sliceEncoder{typ, typ.Elem(), encoder}, nil } diff --git a/feature_reflect_object.go b/feature_reflect_object.go index 7d03aaa..69458cd 100644 --- a/feature_reflect_object.go +++ b/feature_reflect_object.go @@ -49,7 +49,7 @@ func encoderOfStruct(typ reflect.Type) (Encoder, error) { // map is stored as pointer in the struct // but if struct only has one map, it is inlined if field.Type.Kind() == reflect.Map && typ.NumField() > 1 { - encoder = &optionalEncoder{field.Type, encoder} + encoder = &optionalEncoder{encoder} } } for _, fieldName := range fieldNames { diff --git a/jsoniter_reflect_struct_test.go b/jsoniter_reflect_struct_test.go index c58bd03..f28519b 100644 --- a/jsoniter_reflect_struct_test.go +++ b/jsoniter_reflect_struct_test.go @@ -186,3 +186,17 @@ func Test_any_within_struct(t *testing.T) { should.Equal("hello", obj.Field1.ToString()) should.Equal("[1,2,3]", obj.Field2.ToString()) } + +func Test_recursive_struct(t *testing.T) { + should := require.New(t) + type TestObject struct { + Field1 string + Me *TestObject + } + obj := TestObject{} + str, err := MarshalToString(obj) + should.Nil(err) + should.Equal(`{"Field1":"","Me":null}`, str) + err = UnmarshalFromString(str, &obj) + should.Nil(err) +}