1
0
mirror of https://github.com/json-iterator/go.git synced 2025-05-28 22:17:38 +02:00

support recursive type

This commit is contained in:
Tao Wen 2017-05-05 16:51:05 +08:00
parent 6bd835aeb1
commit 91b9e828b7
4 changed files with 71 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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