1
0
mirror of https://github.com/json-iterator/go.git synced 2025-05-28 22:17:38 +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 { type mapDecoder struct {
mapType reflect.Type mapType reflect.Type
elemType 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. // emptyInterface is the header for an interface{} value.
type emptyInterface struct { type emptyInterface struct {
typ *struct{} typ *struct{}
@ -285,9 +324,6 @@ func (iter *Iterator) ReadVal(obj interface{}) {
func (stream *Stream) WriteVal(val interface{}) { func (stream *Stream) WriteVal(val interface{}) {
typ := reflect.TypeOf(val) typ := reflect.TypeOf(val)
cacheKey := typ cacheKey := typ
if typ.Kind() == reflect.Ptr {
cacheKey = typ.Elem()
}
cachedEncoder := getEncoderFromCache(cacheKey) cachedEncoder := getEncoderFromCache(cacheKey)
if cachedEncoder == nil { if cachedEncoder == nil {
encoder, err := encoderOfType(cacheKey) encoder, err := encoderOfType(cacheKey)
@ -298,8 +334,13 @@ func (stream *Stream) WriteVal(val interface{}) {
cachedEncoder = encoder cachedEncoder = encoder
addEncoderToCache(cacheKey, encoder) addEncoderToCache(cacheKey, encoder)
} }
e := (*emptyInterface)(unsafe.Pointer(&val)) 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 type prefix string
@ -365,14 +406,12 @@ func decoderOfType(typ reflect.Type) (Decoder, error) {
case reflect.Map: case reflect.Map:
return prefix("[map]").addToDecoder(decoderOfMap(typ)) return prefix("[map]").addToDecoder(decoderOfMap(typ))
case reflect.Ptr: case reflect.Ptr:
return prefix("[optional]").addToDecoder(decoderOfOptional(typ.Elem())) return prefix("[optional]").addToDecoder(decoderOfOptional(typ))
default: default:
return nil, fmt.Errorf("unsupported type: %v", typ) return nil, fmt.Errorf("unsupported type: %v", typ)
} }
} }
func encoderOfType(typ reflect.Type) (Encoder, error) { func encoderOfType(typ reflect.Type) (Encoder, error) {
typeName := typ.String() typeName := typ.String()
switch typ.Kind() { switch typ.Kind() {
@ -408,17 +447,31 @@ func encoderOfType(typ reflect.Type) (Encoder, error) {
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(typ)) return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(typ))
case reflect.Slice: case reflect.Slice:
return prefix("[slice]").addToEncoder(encoderOfSlice(typ)) 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: default:
return nil, fmt.Errorf("unsupported type: %v", typ) return nil, fmt.Errorf("unsupported type: %v", typ)
} }
} }
func decoderOfOptional(typ reflect.Type) (Decoder, error) { func decoderOfOptional(typ reflect.Type) (Decoder, error) {
decoder, err := decoderOfType(typ) elemType := typ.Elem()
decoder, err := decoderOfType(elemType)
if err != nil { if err != nil {
return nil, err 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) { func decoderOfMap(typ reflect.Type) (Decoder, error) {
@ -429,3 +482,12 @@ func decoderOfMap(typ reflect.Type) (Decoder, error) {
mapInterface := reflect.New(typ).Interface() mapInterface := reflect.New(typ).Interface()
return &mapDecoder{typ, typ.Elem(), decoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil 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" "fmt"
"reflect" "reflect"
"testing" "testing"
"github.com/json-iterator/go/require"
) )
func Test_read_map(t *testing.T) { func Test_read_map(t *testing.T) {
@ -36,3 +37,11 @@ func Test_read_map_of_any(t *testing.T) {
t.Fatal(m) 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) 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) { func Test_decode_struct_field_with_tag(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {