Permalink
Browse files

encode optional

  • Loading branch information...
1 parent 90fc0b8 commit 1f0a0e9a2ddf76753f3189ce300845344e06ca6e @taowen taowen committed Jan 9, 2017
Showing with 125 additions and 21 deletions.
  1. +71 −9 feature_reflect.go
  2. +9 −0 jsoniter_map_test.go
  3. +45 −0 jsoniter_optional_test.go
  4. +0 −12 jsoniter_reflect_struct_test.go
View
@@ -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
+}
@@ -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)
+}
@@ -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)
+}
@@ -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 {

0 comments on commit 1f0a0e9

Please sign in to comment.