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:
parent
90fc0b822f
commit
1f0a0e9a2d
@ -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)
|
||||
}
|
||||
|
45
jsoniter_optional_test.go
Normal file
45
jsoniter_optional_test.go
Normal 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)
|
||||
}
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user