1
0
mirror of https://github.com/json-iterator/go.git synced 2024-11-24 08:22:14 +02:00

encode optional

This commit is contained in:
Tao Wen 2017-01-09 20:51:09 +08:00
parent 90fc0b822f
commit 1f0a0e9a2d
4 changed files with 125 additions and 21 deletions

View File

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

View File

@ -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
jsoniter_optional_test.go Normal file
View File

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

View File

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