diff --git a/api_tests/config_test.go b/api_tests/config_test.go index c4b71fe..75e9215 100644 --- a/api_tests/config_test.go +++ b/api_tests/config_test.go @@ -35,4 +35,14 @@ func Test_customize_tag_key(t *testing.T) { str, err := json.MarshalToString(TestObject{"hello"}) should.Nil(err) should.Equal(`{"field":"hello"}`, str) +} + +func Test_read_large_number_as_interface(t *testing.T) { + should := require.New(t) + var val interface{} + err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val) + should.Nil(err) + output, err := jsoniter.MarshalToString(val) + should.Nil(err) + should.Equal(`123456789123456789123456789`, output) } \ No newline at end of file diff --git a/config.go b/config.go index b5dbb31..79d4b3d 100644 --- a/config.go +++ b/config.go @@ -2,7 +2,6 @@ package jsoniter import ( "encoding/json" - "errors" "io" "reflect" "unsafe" @@ -267,11 +266,6 @@ func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error { data = data[:lastNotSpacePos(data)] iter := cfg.BorrowIterator(data) defer cfg.ReturnIterator(iter) - typ := reflect.TypeOf(v) - if typ.Kind() != reflect.Ptr { - // return non-pointer error - return errors.New("the second param must be ptr type") - } iter.ReadVal(v) if iter.head == iter.tail { iter.loadMore() diff --git a/misc_tests/jsoniter_float_test.go b/misc_tests/jsoniter_float_test.go index 36fd0ef..d9c449d 100644 --- a/misc_tests/jsoniter_float_test.go +++ b/misc_tests/jsoniter_float_test.go @@ -49,13 +49,13 @@ func Test_read_float64_cursor(t *testing.T) { func Test_read_float_scientific(t *testing.T) { should := require.New(t) var obj interface{} - should.Nil(jsoniter.UnmarshalFromString(`1e1`, &obj)) + should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj)) should.Equal(float64(10), obj) - should.Nil(json.Unmarshal([]byte(`1e1`), &obj)) + should.NoError(json.Unmarshal([]byte(`1e1`), &obj)) should.Equal(float64(10), obj) - should.Nil(jsoniter.UnmarshalFromString(`1.0e1`, &obj)) + should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj)) should.Equal(float64(10), obj) - should.Nil(json.Unmarshal([]byte(`1.0e1`), &obj)) + should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj)) should.Equal(float64(10), obj) } diff --git a/misc_tests/jsoniter_interface_test.go b/misc_tests/jsoniter_interface_test.go index 0383b40..7e40fce 100644 --- a/misc_tests/jsoniter_interface_test.go +++ b/misc_tests/jsoniter_interface_test.go @@ -2,36 +2,15 @@ package misc_tests import ( "encoding/json" - "fmt" "testing" "github.com/stretchr/testify/require" "github.com/json-iterator/go" + "io" ) -type MyInterface interface { - Hello() string -} - -type MyString string - -func (ms MyString) Hello() string { - return string(ms) -} - -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(jsoniter.UnmarshalFromString(`{"Field": "hello"}`, &obj)) - should.Equal(MyString("hello"), obj.Field) -} - func Test_nil_non_empty_interface(t *testing.T) { type TestObject struct { - Field []MyInterface + Field []io.Closer } should := require.New(t) obj := TestObject{} @@ -40,31 +19,6 @@ func Test_nil_non_empty_interface(t *testing.T) { should.NotNil(jsoniter.Unmarshal(b, &obj)) } -func Test_read_large_number_as_interface(t *testing.T) { - should := require.New(t) - var val interface{} - err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val) - should.Nil(err) - output, err := jsoniter.MarshalToString(val) - should.Nil(err) - should.Equal(`123456789123456789123456789`, output) -} - -func Test_unmarshal_ptr_to_interface(t *testing.T) { - type TestData struct { - Name string `json:"name"` - } - should := require.New(t) - var obj interface{} = &TestData{} - err := json.Unmarshal([]byte(`{"name":"value"}`), &obj) - should.Nil(err) - should.Equal("&{value}", fmt.Sprintf("%v", obj)) - obj = interface{}(&TestData{}) - err = jsoniter.Unmarshal([]byte(`{"name":"value"}`), &obj) - should.Nil(err) - should.Equal("&{value}", fmt.Sprintf("%v", obj)) -} - func Test_nil_out_null_interface(t *testing.T) { type TestData struct { Field interface{} `json:"field"` @@ -86,7 +40,7 @@ func Test_nil_out_null_interface(t *testing.T) { err = jsoniter.Unmarshal(data2, &obj) should.NoError(err) - should.Equal(nil, obj.Field) + should.Nil(obj.Field) // Checking stdlib behavior matches. obj2 := TestData{ @@ -118,12 +72,12 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) { } err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) - should.Equal(nil, err) + should.NoError(err) should.Equal(&payload, wrapper.Payload) should.Equal(42, (*(wrapper.Payload.(**Payload))).Value) err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) - should.Equal(nil, err) + should.NoError(err) should.Equal(&payload, wrapper.Payload) should.Equal((*Payload)(nil), payload) @@ -138,7 +92,7 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) { should.Equal(42, (*(wrapper.Payload.(**Payload))).Value) err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper) - should.Equal(nil, err) + should.NoError(err) should.Equal(&payload, wrapper.Payload) should.Equal((*Payload)(nil), payload) } @@ -159,11 +113,11 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) { } err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) - should.Equal(nil, err) + should.NoError(err) should.Equal(42, (*(wrapper.Payload.(*Payload))).Value) err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) - should.Equal(nil, err) + should.NoError(err) should.Equal(nil, wrapper.Payload) should.Equal(42, payload.Value) @@ -198,12 +152,12 @@ func Test_unmarshal_into_nil(t *testing.T) { } err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) - should.Nil(err) + should.NoError(err) should.NotNil(wrapper.Payload) should.Nil(payload) err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper) - should.Nil(err) + should.NoError(err) should.Nil(wrapper.Payload) should.Nil(payload) @@ -213,12 +167,12 @@ func Test_unmarshal_into_nil(t *testing.T) { } err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper) - should.Nil(err) + should.NoError(err) should.NotNil(wrapper.Payload) should.Nil(payload) err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper) - should.Nil(err) + should.NoError(err) should.Nil(wrapper.Payload) should.Nil(payload) } diff --git a/reflect.go b/reflect.go index 1cdb954..a39785b 100644 --- a/reflect.go +++ b/reflect.go @@ -36,14 +36,17 @@ type checkIsEmpty interface { // ReadVal copy the underlying JSON into go interface, same as json.Unmarshal func (iter *Iterator) ReadVal(obj interface{}) { typ := reflect.TypeOf(obj) - cacheKey := typ.Elem() - decoder := decoderOfType(iter.cfg, "", cacheKey) - e := (*emptyInterface)(unsafe.Pointer(&obj)) - if e.word == nil { + if typ.Kind() != reflect.Ptr { + iter.ReportError("ReadVal", "can only unmarshal into pointer") + return + } + decoder := iter.cfg.DecoderOf(typ) + ptr := reflect2.PtrOf(obj) + if ptr == nil { iter.ReportError("ReadVal", "can not read into nil pointer") return } - decoder.Decode(e.word, iter) + decoder.Decode(ptr, iter) } // WriteVal copy the go interface into underlying JSON, same as json.Marshal @@ -63,7 +66,7 @@ func (cfg *frozenConfig) DecoderOf(typ reflect.Type) ValDecoder { if decoder != nil { return decoder } - decoder = decoderOfType(cfg, "", typ) + decoder = decoderOfType(cfg, "", typ.Elem()) cfg.addDecoderToCache(cacheKey, decoder) return decoder } @@ -106,10 +109,11 @@ func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val } switch typ.Kind() { case reflect.Interface: - if typ.NumMethod() == 0 { - return &emptyInterfaceCodec{} + if typ.NumMethod() > 0 { + ifaceType := reflect2.Type2(typ).(*reflect2.UnsafeIFaceType) + return &ifaceDecoder{valType: ifaceType} } - return &nonEmptyInterfaceCodec{} + return &efaceDecoder{} case reflect.Struct: return decoderOfStruct(cfg, prefix, typ) case reflect.Array: @@ -239,27 +243,3 @@ func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool { return false } - -func extractInterface(val interface{}) emptyInterface { - return *((*emptyInterface)(unsafe.Pointer(&val))) -} - -// emptyInterface is the header for an interface{} value. -type emptyInterface 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 -} diff --git a/reflect_dynamic.go b/reflect_dynamic.go new file mode 100644 index 0000000..86b8425 --- /dev/null +++ b/reflect_dynamic.go @@ -0,0 +1,70 @@ +package jsoniter + +import ( + "github.com/v2pro/plz/reflect2" + "unsafe" + "reflect" +) + +type dynamicEncoder struct { + valType reflect2.Type +} + +func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := encoder.valType.UnsafeIndirect(ptr) + stream.WriteVal(obj) +} + +func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.valType.UnsafeIndirect(ptr) == nil +} + +type efaceDecoder struct { +} + +func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + pObj := (*interface{})(ptr) + obj := *pObj + if obj == nil { + *pObj = iter.Read() + return + } + typ := reflect2.TypeOf(obj) + if typ.Kind() != reflect.Ptr { + *pObj = iter.Read() + return + } + ptrType := typ.(*reflect2.UnsafePtrType) + ptrElemType := ptrType.Elem() + if iter.WhatIsNext() == NilValue { + if ptrElemType.Kind() != reflect.Ptr { + iter.skipFourBytes('n', 'u', 'l', 'l') + *pObj = nil + return + } + } + if reflect2.IsNil(obj) { + obj := ptrElemType.New() + iter.ReadVal(obj) + *pObj = obj + return + } + iter.ReadVal(obj) +} + +type ifaceDecoder struct { + valType *reflect2.UnsafeIFaceType +} + +func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.ReadNil() { + decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew()) + return + } + obj := decoder.valType.UnsafeIndirect(ptr) + if reflect2.IsNil(obj) { + iter.ReportError("decode non empty interface", "can not unmarshal into nil") + return + } + iter.ReadVal(obj) +} diff --git a/reflect_extension.go b/reflect_extension.go index c9c3ba0..48f6b75 100644 --- a/reflect_extension.go +++ b/reflect_extension.go @@ -7,6 +7,7 @@ import ( "strings" "unicode" "unsafe" + "github.com/v2pro/plz/reflect2" ) var typeDecoders = map[string]ValDecoder{} @@ -240,7 +241,7 @@ func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecode if typ.Kind() == reflect.Ptr { decoder := typeDecoders[typ.Elem().String()] if decoder != nil { - return &OptionalDecoder{typ.Elem(), decoder} + return &OptionalDecoder{reflect2.Type2(typ.Elem()), decoder} } } return nil @@ -317,7 +318,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD omitempty := binding.Encoder.(*structFieldEncoder).omitempty binding.Encoder = &dereferenceEncoder{binding.Encoder} binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty} - binding.Decoder = &dereferenceDecoder{field.Type.Elem(), binding.Decoder} + binding.Decoder = &dereferenceDecoder{reflect2.Type2(field.Type.Elem()), binding.Decoder} binding.Decoder = &structFieldDecoder{&field, binding.Decoder} embeddedBindings = append(embeddedBindings, binding) } diff --git a/reflect_native.go b/reflect_native.go index 8e6eb18..6cbede6 100644 --- a/reflect_native.go +++ b/reflect_native.go @@ -435,112 +435,6 @@ func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool { return !(*((*bool)(ptr))) } -type emptyInterfaceCodec struct { -} - -func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - existing := *((*interface{})(ptr)) - - // Checking for both typed and untyped nil pointers. - if existing != nil && - reflect.TypeOf(existing).Kind() == reflect.Ptr && - !reflect.ValueOf(existing).IsNil() { - - var ptrToExisting interface{} - for { - elem := reflect.ValueOf(existing).Elem() - if elem.Kind() != reflect.Ptr || elem.IsNil() { - break - } - ptrToExisting = existing - existing = elem.Interface() - } - - if iter.ReadNil() { - if ptrToExisting != nil { - nilPtr := reflect.Zero(reflect.TypeOf(ptrToExisting).Elem()) - reflect.ValueOf(ptrToExisting).Elem().Set(nilPtr) - } else { - *((*interface{})(ptr)) = nil - } - } else { - iter.ReadVal(existing) - } - - return - } - - if iter.ReadNil() { - *((*interface{})(ptr)) = nil - } else { - *((*interface{})(ptr)) = iter.Read() - } -} - -func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := *((*interface{})(ptr)) - stream.WriteVal(obj) -} - -func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool { - emptyInterface := (*emptyInterface)(ptr) - return emptyInterface.typ == nil -} - -type nonEmptyInterfaceCodec struct { -} - -func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.WhatIsNext() == NilValue { - iter.skipFourBytes('n', 'u', 'l', 'l') - *((*interface{})(ptr)) = nil - return - } - nonEmptyInterface := (*nonEmptyInterface)(ptr) - if nonEmptyInterface.itab == nil { - iter.ReportError("read non-empty interface", "do not know which concrete type to decode to") - return - } - var i interface{} - e := (*emptyInterface)(unsafe.Pointer(&i)) - e.typ = nonEmptyInterface.itab.typ - e.word = nonEmptyInterface.word - iter.ReadVal(&i) - if e.word == nil { - nonEmptyInterface.itab = nil - } - nonEmptyInterface.word = e.word -} - -func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - nonEmptyInterface := (*nonEmptyInterface)(ptr) - var i interface{} - if nonEmptyInterface.itab != nil { - e := (*emptyInterface)(unsafe.Pointer(&i)) - e.typ = nonEmptyInterface.itab.typ - e.word = nonEmptyInterface.word - } - stream.WriteVal(i) -} - -func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool { - nonEmptyInterface := (*nonEmptyInterface)(ptr) - return nonEmptyInterface.word == nil -} - -type dynamicEncoder struct { - valType reflect2.Type -} - -func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := encoder.valType.UnsafeIndirect(ptr) - stream.WriteVal(obj) -} - -func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.valType.UnsafeIndirect(ptr) == nil -} - type base64Codec struct { sliceType *reflect2.UnsafeSliceType sliceDecoder ValDecoder diff --git a/reflect_optional.go b/reflect_optional.go index 82100cd..86dd15f 100644 --- a/reflect_optional.go +++ b/reflect_optional.go @@ -3,12 +3,16 @@ package jsoniter import ( "reflect" "unsafe" + "github.com/v2pro/plz/reflect2" ) func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder { elemType := typ.Elem() decoder := decoderOfType(cfg, prefix, elemType) - return &OptionalDecoder{elemType, decoder} + if prefix == "" && elemType.Kind() == reflect.Ptr { + return &dereferenceDecoder{reflect2.Type2(elemType), decoder} + } + return &OptionalDecoder{reflect2.Type2(elemType), decoder} } func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder { @@ -19,7 +23,7 @@ func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEn } type OptionalDecoder struct { - ValueType reflect.Type + ValueType reflect2.Type ValueDecoder ValDecoder } @@ -28,12 +32,10 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { *((*unsafe.Pointer)(ptr)) = nil } else { if *((*unsafe.Pointer)(ptr)) == nil { - // TODO: use reflect2 instead //pointer to null, we have to allocate memory to hold the value - value := reflect.New(decoder.ValueType) - newPtr := extractInterface(value.Interface()).word + newPtr := decoder.ValueType.UnsafeNew() decoder.ValueDecoder.Decode(newPtr, iter) - *((*uintptr)(ptr)) = uintptr(newPtr) + *((*unsafe.Pointer)(ptr)) = newPtr } else { //reuse existing instance decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) @@ -43,17 +45,16 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { type dereferenceDecoder struct { // only to deference a pointer - valueType reflect.Type + valueType reflect2.Type valueDecoder ValDecoder } func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { if *((*unsafe.Pointer)(ptr)) == nil { //pointer to null, we have to allocate memory to hold the value - value := reflect.New(decoder.valueType) - newPtr := extractInterface(value.Interface()).word + newPtr := decoder.valueType.UnsafeNew() decoder.valueDecoder.Decode(newPtr, iter) - *((*uintptr)(ptr)) = uintptr(newPtr) + *((*unsafe.Pointer)(ptr)) = newPtr } else { //reuse existing instance decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) @@ -127,4 +128,4 @@ type referenceDecoder struct { func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { decoder.decoder.Decode(unsafe.Pointer(&ptr), iter) -} \ No newline at end of file +} diff --git a/value_tests/eface_test.go b/value_tests/eface_test.go new file mode 100644 index 0000000..8940da4 --- /dev/null +++ b/value_tests/eface_test.go @@ -0,0 +1,78 @@ +package test + +func init() { + var pEFace = func(val interface{}) *interface{} { + return &val + } + var pInt = func(val int) *int { + return &val + } + unmarshalCases = append(unmarshalCases, unmarshalCase{ + ptr: (**interface{})(nil), + input: `"hello"`, + }, unmarshalCase{ + ptr: (**interface{})(nil), + input: `1e1`, + }, unmarshalCase{ + ptr: (**interface{})(nil), + input: `1.0e1`, + }, unmarshalCase{ + ptr: (*[]interface{})(nil), + input: `[1.0e1]`, + }, unmarshalCase{ + ptr: (*struct { + Field interface{} + })(nil), + input: `{"field":"hello"}`, + }, unmarshalCase{ + obj: func() interface{} { + type TestData struct { + Name string `json:"name"` + } + o := &TestData{} + return &o + }, + input: `{"name":"value"}`, + }, unmarshalCase{ + obj: func() interface{} { + b := true + return &struct { + Field interface{} `json:"field"` + }{&b} + }, + input: `{"field": null}`, + }, unmarshalCase{ + obj: func() interface{} { + var pb *bool + return &struct { + Field interface{} `json:"field"` + }{&pb} + }, + input: `{"field": null}`, + }, unmarshalCase{ + obj: func() interface{} { + b := true + pb := &b + return &struct { + Field interface{} `json:"field"` + }{&pb} + }, + input: `{"field": null}`, + }) + marshalCases = append(marshalCases, + pEFace("hello"), + struct { + Field interface{} + }{"hello"}, + struct { + Field interface{} + }{struct{ + field chan int + }{}}, + struct { + Field interface{} + }{struct{ + Field *int + }{pInt(100)}}, + ) +} diff --git a/value_tests/iface_test.go b/value_tests/iface_test.go new file mode 100644 index 0000000..7eff2a8 --- /dev/null +++ b/value_tests/iface_test.go @@ -0,0 +1,45 @@ +package test + +import "io" + +func init() { + var pCloser1 = func(str string) *io.Closer { + closer := io.Closer(strCloser1(str)) + return &closer + } + var pCloser2 = func(str string) *io.Closer { + strCloser := strCloser2(str) + closer := io.Closer(&strCloser) + return &closer + } + marshalCases = append(marshalCases, + pCloser1("hello"), + pCloser2("hello"), + ) + unmarshalCases = append(unmarshalCases, unmarshalCase{ + ptr: (*[]io.Closer)(nil), + input: "[null]", + }, unmarshalCase{ + obj: func() interface{} { + strCloser := strCloser2("") + return &struct { + Field io.Closer + }{ + &strCloser, + } + }, + input: `{"Field": "hello"}`, + }) +} + +type strCloser1 string + +func (closer strCloser1) Close() error { + return nil +} + +type strCloser2 string + +func (closer *strCloser2) Close() error { + return nil +} diff --git a/value_tests/ptr_test.go b/value_tests/ptr_test.go index edc80ee..222b2d4 100644 --- a/value_tests/ptr_test.go +++ b/value_tests/ptr_test.go @@ -1,25 +1,39 @@ package test func init() { - var pEFace = func(val interface{}) *interface{} { - return &val - } var pInt = func(val int) *int { return &val } - unmarshalCases = append(unmarshalCases, unmarshalCase{ - ptr: (**interface{})(nil), - input: `"hello"`, - }, unmarshalCase{ - ptr: (**interface{})(nil), - input: `1e1`, - }, unmarshalCase{ - ptr: (**interface{})(nil), - input: `1.0e1`, - }) marshalCases = append(marshalCases, - pEFace("hello"), (*int)(nil), pInt(100), ) + unmarshalCases = append(unmarshalCases, unmarshalCase{ + obj: func() interface{} { + var i int + return &i + }, + input: "null", + }, unmarshalCase{ + obj: func() interface{} { + var i *int + return &i + }, + input: "10", + }, unmarshalCase{ + obj: func() interface{} { + var i int + pi := &i + return &pi + }, + input: "null", + }, unmarshalCase{ + obj: func() interface{} { + var i int + pi := &i + ppi := &pi + return &ppi + }, + input: "null", + }) } diff --git a/value_tests/value_test.go b/value_tests/value_test.go index 72c83ce..9e8c3ca 100644 --- a/value_tests/value_test.go +++ b/value_tests/value_test.go @@ -10,8 +10,10 @@ import ( ) type unmarshalCase struct { + obj func() interface{} ptr interface{} input string + selected bool } var unmarshalCases []unmarshalCase @@ -25,16 +27,31 @@ type selectedMarshalCase struct { } func Test_unmarshal(t *testing.T) { - should := require.New(t) for _, testCase := range unmarshalCases { - valType := reflect.TypeOf(testCase.ptr).Elem() - ptr1Val := reflect.New(valType) - err1 := json.Unmarshal([]byte(testCase.input), ptr1Val.Interface()) - should.NoError(err1) - ptr2Val := reflect.New(valType) - err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), ptr2Val.Interface()) - should.NoError(err2) - should.Equal(ptr1Val.Interface(), ptr2Val.Interface()) + if testCase.selected { + unmarshalCases = []unmarshalCase{testCase} + break + } + } + for i, testCase := range unmarshalCases { + t.Run(fmt.Sprintf("[%v]%s", i, testCase.input), func(t *testing.T) { + should := require.New(t) + var obj1 interface{} + var obj2 interface{} + if testCase.obj != nil { + obj1 = testCase.obj() + obj2 = testCase.obj() + } else { + valType := reflect.TypeOf(testCase.ptr).Elem() + obj1 = reflect.New(valType).Interface() + obj2 = reflect.New(valType).Interface() + } + err1 := json.Unmarshal([]byte(testCase.input), obj1) + should.NoError(err1, "json") + err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), obj2) + should.NoError(err2, "jsoniter") + should.Equal(obj1, obj2) + }) } }