Kaynağa Gözat

encode optional

Tao Wen 9 yıl önce
ebeveyn
işleme
1f0a0e9a2d

+ 71 - 9
feature_reflect.go

@@ -133,6 +133,19 @@ func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
 	}
 }
 
+type optionalEncoder struct {
+	valueType    reflect.Type
+	valueEncoder Encoder
+}
+
+func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
+	if *((*unsafe.Pointer)(ptr)) == nil {
+		stream.WriteNull()
+	} else {
+		encoder.valueEncoder.encode(*((*unsafe.Pointer)(ptr)), stream)
+	}
+}
+
 type mapDecoder struct {
 	mapType      reflect.Type
 	elemType     reflect.Type
@@ -155,6 +168,32 @@ func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
 	}
 }
 
+type mapEncoder struct {
+	mapType      reflect.Type
+	elemType     reflect.Type
+	elemEncoder  Encoder
+	mapInterface emptyInterface
+}
+
+func (encoder *mapEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
+	mapInterface := encoder.mapInterface
+	mapInterface.word = ptr
+	realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
+	realVal := reflect.ValueOf(*realInterface)
+
+	stream.WriteObjectStart()
+	for i, key := range realVal.MapKeys() {
+		if i != 0 {
+			stream.WriteMore()
+		}
+		stream.WriteObjectField(key.String())
+		val := realVal.MapIndex(key).Interface()
+		e := (*emptyInterface)(unsafe.Pointer(&val))
+		encoder.elemEncoder.encode(e.word, stream)
+	}
+	stream.WriteObjectEnd()
+}
+
 // emptyInterface is the header for an interface{} value.
 type emptyInterface struct {
 	typ  *struct{}
@@ -285,9 +324,6 @@ func (iter *Iterator) ReadVal(obj interface{}) {
 func (stream *Stream) WriteVal(val interface{}) {
 	typ := reflect.TypeOf(val)
 	cacheKey := typ
-	if typ.Kind() == reflect.Ptr {
-		cacheKey = typ.Elem()
-	}
 	cachedEncoder := getEncoderFromCache(cacheKey)
 	if cachedEncoder == nil {
 		encoder, err := encoderOfType(cacheKey)
@@ -298,8 +334,13 @@ func (stream *Stream) WriteVal(val interface{}) {
 		cachedEncoder = encoder
 		addEncoderToCache(cacheKey, encoder)
 	}
+
 	e := (*emptyInterface)(unsafe.Pointer(&val))
-	cachedEncoder.encode(e.word, stream)
+	if typ.Kind() == reflect.Ptr {
+		cachedEncoder.encode(unsafe.Pointer(&e.word), stream)
+	} else {
+		cachedEncoder.encode(e.word, stream)
+	}
 }
 
 type prefix string
@@ -365,14 +406,12 @@ func decoderOfType(typ reflect.Type) (Decoder, error) {
 	case reflect.Map:
 		return prefix("[map]").addToDecoder(decoderOfMap(typ))
 	case reflect.Ptr:
-		return prefix("[optional]").addToDecoder(decoderOfOptional(typ.Elem()))
+		return prefix("[optional]").addToDecoder(decoderOfOptional(typ))
 	default:
 		return nil, fmt.Errorf("unsupported type: %v", typ)
 	}
 }
 
-
-
 func encoderOfType(typ reflect.Type) (Encoder, error) {
 	typeName := typ.String()
 	switch typ.Kind() {
@@ -408,17 +447,31 @@ func encoderOfType(typ reflect.Type) (Encoder, error) {
 		return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(typ))
 	case reflect.Slice:
 		return prefix("[slice]").addToEncoder(encoderOfSlice(typ))
+	case reflect.Map:
+		return prefix("[map]").addToEncoder(encoderOfMap(typ))
+	case reflect.Ptr:
+		return prefix("[optional]").addToEncoder(encoderOfOptional(typ))
 	default:
 		return nil, fmt.Errorf("unsupported type: %v", typ)
 	}
 }
 
 func decoderOfOptional(typ reflect.Type) (Decoder, error) {
-	decoder, err := decoderOfType(typ)
+	elemType := typ.Elem()
+	decoder, err := decoderOfType(elemType)
 	if err != nil {
 		return nil, err
 	}
-	return &optionalDecoder{typ, decoder}, nil
+	return &optionalDecoder{elemType, decoder}, nil
+}
+
+func encoderOfOptional(typ reflect.Type) (Encoder, error) {
+	elemType := typ.Elem()
+	decoder, err := encoderOfType(elemType)
+	if err != nil {
+		return nil, err
+	}
+	return &optionalEncoder{elemType, decoder}, nil
 }
 
 func decoderOfMap(typ reflect.Type) (Decoder, error) {
@@ -429,3 +482,12 @@ func decoderOfMap(typ reflect.Type) (Decoder, error) {
 	mapInterface := reflect.New(typ).Interface()
 	return &mapDecoder{typ, typ.Elem(), decoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
 }
+
+func encoderOfMap(typ reflect.Type) (Encoder, error) {
+	encoder, err := encoderOfType(typ.Elem())
+	if err != nil {
+		return nil, err
+	}
+	mapInterface := reflect.New(typ).Elem().Interface()
+	return &mapEncoder{typ, typ.Elem(), encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
+}

+ 9 - 0
jsoniter_map_test.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"reflect"
 	"testing"
+	"github.com/json-iterator/go/require"
 )
 
 func Test_read_map(t *testing.T) {
@@ -36,3 +37,11 @@ func Test_read_map_of_any(t *testing.T) {
 		t.Fatal(m)
 	}
 }
+
+func Test_write_val_map(t *testing.T) {
+	should := require.New(t)
+	val := map[string]string{"1": "2"}
+	str, err := MarshalToString(val)
+	should.Nil(err)
+	should.Equal(`{"1":"2"}`, str)
+}

+ 45 - 0
jsoniter_optional_test.go

@@ -0,0 +1,45 @@
+package jsoniter
+
+import (
+	"testing"
+	"github.com/json-iterator/go/require"
+)
+
+func Test_encode_optional_int_pointer(t *testing.T) {
+	should := require.New(t)
+	var ptr *int
+	str, err := MarshalToString(ptr)
+	should.Nil(err)
+	should.Equal("null", str)
+	val := 100
+	ptr = &val
+	str, err = MarshalToString(ptr)
+	should.Nil(err)
+	should.Equal("100", str)
+}
+
+func Test_decode_struct_with_optional_field(t *testing.T) {
+	should := require.New(t)
+	type TestObject struct {
+		field1 *string
+		field2 *string
+	}
+	obj := TestObject{}
+	UnmarshalFromString(`{"field1": null, "field2": "world"}`, &obj)
+	should.Nil(obj.field1)
+	should.Equal("world", *obj.field2)
+}
+
+func Test_encode_struct_with_optional_field(t *testing.T) {
+	should := require.New(t)
+	type TestObject struct {
+		field1 *string
+		field2 *string
+	}
+	obj := TestObject{}
+	world := "world"
+	obj.field2 = &world
+	str, err := MarshalToString(obj)
+	should.Nil(err)
+	should.Equal(`{"field1":null,"field2":"world"}`, str)
+}

+ 0 - 12
jsoniter_reflect_struct_test.go

@@ -85,18 +85,6 @@ func Test_decode_five_fields_struct(t *testing.T) {
 	should.Equal("e", obj.field5)
 }
 
-func Test_decode_struct_with_optional_field(t *testing.T) {
-	should := require.New(t)
-	type TestObject struct {
-		field1 *string
-		field2 *string
-	}
-	obj := TestObject{}
-	UnmarshalFromString(`{"field1": null, "field2": "world"}`, &obj)
-	should.Nil(obj.field1)
-	should.Equal("world", *obj.field2)
-}
-
 func Test_decode_struct_field_with_tag(t *testing.T) {
 	should := require.New(t)
 	type TestObject struct {