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:
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 {
|
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
|
||||||
|
}
|
||||||
|
@ -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
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)
|
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user