mirror of
https://github.com/json-iterator/go.git
synced 2025-03-23 21:09:11 +02:00
support non empty interface
This commit is contained in:
parent
a7a7c7879a
commit
707ed3b091
@ -5,7 +5,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
"errors"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -235,7 +234,21 @@ func (decoder *placeholderDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
|
|
||||||
// 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 unsafe.Pointer
|
||||||
|
word unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyInterface is the header for an interface with method (not interface{})
|
||||||
|
type nonEmptyInterface struct {
|
||||||
|
// see ../runtime/iface.go:/Itab
|
||||||
|
itab *struct {
|
||||||
|
ityp unsafe.Pointer // static interface type
|
||||||
|
typ unsafe.Pointer // dynamic concrete type
|
||||||
|
link unsafe.Pointer
|
||||||
|
bad int32
|
||||||
|
unused int32
|
||||||
|
fun [100000]unsafe.Pointer // method table
|
||||||
|
}
|
||||||
word unsafe.Pointer
|
word unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,9 +371,9 @@ func createDecoderOfType(typ reflect.Type) (Decoder, error) {
|
|||||||
return &boolCodec{}, nil
|
return &boolCodec{}, nil
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
if typ.NumMethod() == 0 {
|
||||||
return &interfaceCodec{}, nil
|
return &emptyInterfaceCodec{}, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("unsupportd type: " + typ.String())
|
return &nonEmptyInterfaceCodec{}, nil
|
||||||
}
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ))
|
return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ))
|
||||||
@ -408,7 +421,8 @@ func createEncoderOfType(typ reflect.Type) (Encoder, error) {
|
|||||||
templateInterface := reflect.New(typ).Elem().Interface()
|
templateInterface := reflect.New(typ).Elem().Interface()
|
||||||
return &marshalerEncoder{extractInterface(templateInterface)}, nil
|
return &marshalerEncoder{extractInterface(templateInterface)}, nil
|
||||||
}
|
}
|
||||||
switch typ.Kind() {
|
kind := typ.Kind()
|
||||||
|
switch kind {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return &stringCodec{}, nil
|
return &stringCodec{}, nil
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
@ -438,7 +452,11 @@ func createEncoderOfType(typ reflect.Type) (Encoder, error) {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return &boolCodec{}, nil
|
return &boolCodec{}, nil
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
return &interfaceCodec{}, nil
|
if typ.NumMethod() == 0 {
|
||||||
|
return &emptyInterfaceCodec{}, nil
|
||||||
|
} else {
|
||||||
|
return &nonEmptyInterfaceCodec{}, nil
|
||||||
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ))
|
return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ))
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
|
@ -272,25 +272,56 @@ func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return !(*((*bool)(ptr)))
|
return !(*((*bool)(ptr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
type interfaceCodec struct {
|
type emptyInterfaceCodec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec *interfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
func (codec *emptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
*((*interface{})(ptr)) = iter.Read()
|
*((*interface{})(ptr)) = iter.Read()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec *interfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
func (codec *emptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
stream.WriteVal(*((*interface{})(ptr)))
|
stream.WriteVal(*((*interface{})(ptr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *interfaceCodec) encodeInterface(val interface{}, stream *Stream) {
|
func (encoder *emptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||||
stream.WriteVal(val)
|
stream.WriteVal(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec *interfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
|
func (codec *emptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||||
return ptr == nil
|
return ptr == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nonEmptyInterfaceCodec struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *nonEmptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
|
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||||
|
var i interface{}
|
||||||
|
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||||
|
e.typ = nonEmptyInterface.itab.typ
|
||||||
|
e.word = nonEmptyInterface.word
|
||||||
|
iter.ReadVal(&i)
|
||||||
|
nonEmptyInterface.word = e.word
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *nonEmptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||||
|
var i interface{}
|
||||||
|
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||||
|
e.typ = nonEmptyInterface.itab.typ
|
||||||
|
e.word = nonEmptyInterface.word
|
||||||
|
stream.WriteVal(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (encoder *nonEmptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) {
|
||||||
|
stream.WriteVal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (codec *nonEmptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
|
||||||
|
nonEmptyInterface := (*nonEmptyInterface)(ptr)
|
||||||
|
return nonEmptyInterface.word == nil
|
||||||
|
}
|
||||||
|
|
||||||
type anyCodec struct {
|
type anyCodec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,4 +212,4 @@ func Test_unmarshaler_and_decoder(t *testing.T) {
|
|||||||
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
|
err = Unmarshal([]byte(`{"Field":"hello"}`), &obj)
|
||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal(10, int(*obj.Field))
|
should.Equal(10, int(*obj.Field))
|
||||||
}
|
}
|
@ -91,4 +91,50 @@ func Test_read_custom_interface(t *testing.T) {
|
|||||||
err := UnmarshalFromString(`"hello"`, &val)
|
err := UnmarshalFromString(`"hello"`, &val)
|
||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal("hello", val.Hello())
|
should.Equal("hello", val.Hello())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_decode_object_contain_empty_interface(t *testing.T) {
|
||||||
|
type TestObject struct {
|
||||||
|
Field interface{}
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
obj := TestObject{}
|
||||||
|
obj.Field = 1024
|
||||||
|
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
|
||||||
|
should.Equal("hello", obj.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_decode_object_contain_non_empty_interface(t *testing.T) {
|
||||||
|
type TestObject struct {
|
||||||
|
Field MyInterface
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
obj := TestObject{}
|
||||||
|
obj.Field = MyString("abc")
|
||||||
|
should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj))
|
||||||
|
should.Equal(MyString("hello"), obj.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encode_object_contain_empty_interface(t *testing.T) {
|
||||||
|
type TestObject struct {
|
||||||
|
Field interface{}
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
obj := TestObject{}
|
||||||
|
obj.Field = 1024
|
||||||
|
str, err := MarshalToString(obj)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`{"Field":1024}`, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encode_object_contain_non_empty_interface(t *testing.T) {
|
||||||
|
type TestObject struct {
|
||||||
|
Field MyInterface
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
obj := TestObject{}
|
||||||
|
obj.Field = MyString("hello")
|
||||||
|
str, err := MarshalToString(obj)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`{"Field":"hello"}`, str)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user