1
0
mirror of https://github.com/json-iterator/go.git synced 2025-06-15 22:50:24 +02:00

20 Commits
1.0.0 ... 1.0.1

Author SHA1 Message Date
92772579dd Merge pull request #170 from olegshaldybin/marshal-enum-pointer
Fix custom marshaler for enum types
2017-09-15 09:08:45 -05:00
ae57d167e8 Fix custom marshaler for enum types
When MarshalJSON was defined on a pointer receiver custom enum type
marshaling/unmarshaling was panicing since the underlying primitive type
was treated as a pointer.

Since method set for pointer receivers includes value receiver methods
we don't really need optionalEncoder and can just use marshalEncoder
directly.
2017-09-14 23:26:12 -07:00
eef35e549b Merge pull request #169 from toffaletti/fix-nil-interface
Fix handling of nil empty interface
2017-09-15 00:45:11 -05:00
005d86dc44 fix handling of nil empty interface 2017-09-14 21:32:42 -07:00
e658f6597a add failing test for handling of nil interface with omitempty 2017-09-14 20:44:42 -07:00
f8eb43eda3 Merge pull request #168 from olegshaldybin/null-booleans
Allow null booleans
2017-09-14 20:58:31 -05:00
18a241d40b Allow null booleans
Make sure we do the same thing as stdlib with null booleans by not
touching the original value and discarding the null.

Another somewhat related change is nulling out null interface values in
the original structure. This also matches stdlib behavior.
2017-09-14 16:47:35 -07:00
0fdf883ac0 Merge pull request #167 from olegshaldybin/shorter-sleep
Shorter sleep while waiting for encoder/decoder
2017-09-14 18:23:29 -05:00
34fbec74ad Shorter sleep while waiting for encoder/decoder
If the client is using the same jsoniter config with multiple goroutines
it's very likely that few initial operations will encounter a placeholder
encoder/decoder while the real one is being created by another
goroutine. Having a full second sleep seems too conservative, since
encoder/decoder will be created in a very short time. This is very easy
to reproduce in any real environment with a few concurrent requests of
the same type. A few initial requests will have 1s+ response time.

Changing to 10ms should smooth out marshal/unmarshal times for these
initial concurrent requests.
2017-09-14 12:37:47 -07:00
90574c5ca3 #166 support ValidateJsonRawMessage in ConfigCompatibleWithStandardLibrary 2017-09-14 23:54:40 +08:00
6a4ba7bfa9 Merge branch 'master' of https://github.com/json-iterator/go 2017-09-09 08:46:07 +08:00
0828e559d0 #164 support interface{} with ptr 2017-09-09 08:45:57 +08:00
2c67d0f68a Merge pull request #163 from dvrkps/patch-2
travis: add 1.x to go versions
2017-09-07 11:25:07 -05:00
f29a0391bc travis: add 1.x to go versions 2017-09-07 17:12:42 +02:00
374e68a144 Merge pull request #162 from cch123/fix-bool-to-number
fix fuzzy decoder from bool value to number
2017-09-06 00:19:18 -05:00
b134d86290 optimize code 2017-09-06 13:18:05 +08:00
bc3221879d fix fuzzy decoder from bool value to number 2017-09-06 12:31:56 +08:00
8c7fc7584a #159 fix fuzzy decoder, the newIter assigned io.EOF error to original iterator, which stopped further processing 2017-09-06 00:31:25 +08:00
db32ee8c2d #157 number can be null 2017-09-05 13:00:03 +08:00
d80309af3b #156 invoke Marshaler defined on pointer types 2017-09-01 15:44:12 +08:00
15 changed files with 472 additions and 20 deletions

View File

@ -2,6 +2,7 @@ language: go
go: go:
- 1.8.x - 1.8.x
- 1.x
before_install: before_install:
- go get -t -v ./... - go get -t -v ./...

View File

@ -2,11 +2,13 @@ package extra
import ( import (
"encoding/json" "encoding/json"
"github.com/json-iterator/go" "io"
"math" "math"
"reflect" "reflect"
"strings" "strings"
"unsafe" "unsafe"
"github.com/json-iterator/go"
) )
const maxUint = ^uint(0) const maxUint = ^uint(0)
@ -199,6 +201,12 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
str = string(number) str = string(number)
case jsoniter.StringValue: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
case jsoniter.BoolValue:
if iter.ReadBool() {
str = "1"
} else {
str = "0"
}
default: default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string") iter.ReportError("fuzzyIntegerDecoder", "not number or string")
} }
@ -206,7 +214,7 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1 isFloat := strings.IndexByte(str, '.') != -1
decoder.fun(isFloat, ptr, newIter) decoder.fun(isFloat, ptr, newIter)
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
} }
@ -225,9 +233,16 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float32)(ptr)) = newIter.ReadFloat32() *((*float32)(ptr)) = newIter.ReadFloat32()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float32
if iter.ReadBool() {
*((*float32)(ptr)) = 1
} else {
*((*float32)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }
@ -247,9 +262,16 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float64)(ptr)) = newIter.ReadFloat64() *((*float64)(ptr)) = newIter.ReadFloat64()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float64
if iter.ReadBool() {
*((*float64)(ptr)) = 1
} else {
*((*float64)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }

View File

@ -38,6 +38,12 @@ func Test_any_to_int64(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int64(10), val) should.Equal(int64(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int64(1), val)
should.Nil(jsoniter.UnmarshalFromString(`-10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(int64(-10), val) should.Equal(int64(-10), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
@ -57,6 +63,13 @@ func Test_any_to_int(t *testing.T) {
should.Equal(10, val) should.Equal(10, val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(10, val) should.Equal(10, val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(0, val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(1, val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -74,6 +87,13 @@ func Test_any_to_int16(t *testing.T) {
should.Equal(int16(10), val) should.Equal(int16(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int16(10), val) should.Equal(int16(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int16(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int16(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -91,6 +111,13 @@ func Test_any_to_int32(t *testing.T) {
should.Equal(int32(10), val) should.Equal(int32(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int32(10), val) should.Equal(int32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int32(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -108,6 +135,13 @@ func Test_any_to_int8(t *testing.T) {
should.Equal(int8(10), val) should.Equal(int8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int8(10), val) should.Equal(int8(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(int8(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(int8(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -125,6 +159,13 @@ func Test_any_to_uint8(t *testing.T) {
should.Equal(uint8(10), val) should.Equal(uint8(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint8(10), val) should.Equal(uint8(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint8(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint8(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -144,6 +185,12 @@ func Test_any_to_uint64(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint64(10), val) should.Equal(uint64(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint64(1), val)
// TODO fix? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint64(0), val) should.Equal(uint64(0), val)
@ -165,6 +212,12 @@ func Test_any_to_uint32(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint32(10), val) should.Equal(uint32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint32(1), val)
// TODO fix? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint32(0), val) should.Equal(uint32(0), val)
@ -186,6 +239,12 @@ func Test_any_to_uint16(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint16(10), val) should.Equal(uint16(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint16(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint16(1), val)
// TODO fix? // TODO fix?
should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val)) should.NotNil(jsoniter.UnmarshalFromString(`-10`, &val))
should.Equal(uint16(0), val) should.Equal(uint16(0), val)
@ -205,6 +264,12 @@ func Test_any_to_uint(t *testing.T) {
should.Equal(uint(10), val) should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(uint(10), val) should.Equal(uint(10), val)
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(uint(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(uint(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
// large float to int // large float to int
@ -223,6 +288,13 @@ func Test_any_to_float32(t *testing.T) {
should.Equal(float32(10.1), val) should.Equal(float32(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float32(10), val) should.Equal(float32(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(float32(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(float32(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
} }
@ -240,6 +312,13 @@ func Test_any_to_float64(t *testing.T) {
should.Equal(float64(10.1), val) should.Equal(float64(10.1), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val)) should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(float64(10), val) should.Equal(float64(10), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
should.Equal(float64(0), val)
should.Nil(jsoniter.UnmarshalFromString(`true`, &val))
should.Equal(float64(1), val)
should.NotNil(jsoniter.UnmarshalFromString("{}", &val)) should.NotNil(jsoniter.UnmarshalFromString("{}", &val))
should.NotNil(jsoniter.UnmarshalFromString("[]", &val)) should.NotNil(jsoniter.UnmarshalFromString("[]", &val))
} }
@ -257,3 +336,24 @@ func Test_empty_array_as_object(t *testing.T) {
should.Nil(jsoniter.UnmarshalFromString(`[]`, &val)) should.Nil(jsoniter.UnmarshalFromString(`[]`, &val))
should.Equal(struct{}{}, val) should.Equal(struct{}{}, val)
} }
func Test_bad_case(t *testing.T) {
var jsonstr = `
{
"extra_type": 181760,
"combo_type": 0,
"trigger_time_ms": 1498800398000,
"_create_time": "2017-06-16 11:21:39",
"_msg_type": 41000
}
`
type OrderEventRequestParams struct {
ExtraType uint64 `json:"extra_type"`
}
var a OrderEventRequestParams
err := jsoniter.UnmarshalFromString(jsonstr, &a)
should := require.New(t)
should.Nil(err)
}

View File

@ -18,6 +18,7 @@ type Config struct {
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
TagKey string TagKey string
ValidateJsonRawMessage bool
} }
type frozenConfig struct { type frozenConfig struct {
@ -55,6 +56,7 @@ var ConfigDefault = Config{
var ConfigCompatibleWithStandardLibrary = Config{ var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true, EscapeHTML: true,
SortMapKeys: true, SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze() }.Froze()
// ConfigFastest marshals float with only 6 digits precision // ConfigFastest marshals float with only 6 digits precision
@ -83,10 +85,31 @@ func (cfg Config) Froze() API {
if cfg.UseNumber { if cfg.UseNumber {
frozenConfig.useNumber() frozenConfig.useNumber()
} }
if cfg.ValidateJsonRawMessage {
frozenConfig.validateJsonRawMessage()
}
frozenConfig.configBeforeFrozen = cfg frozenConfig.configBeforeFrozen = cfg
return frozenConfig return frozenConfig
} }
func (cfg *frozenConfig) validateJsonRawMessage() {
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
rawMessage := *(*json.RawMessage)(ptr)
iter := cfg.BorrowIterator([]byte(rawMessage))
iter.Read()
if iter.Error != nil {
stream.WriteRaw("null")
} else {
cfg.ReturnIterator(iter)
stream.WriteRaw(string(rawMessage))
}
}, func(ptr unsafe.Pointer) bool {
return false
}}
cfg.addEncoderToCache(reflect.TypeOf((*json.RawMessage)(nil)).Elem(), encoder)
cfg.addEncoderToCache(reflect.TypeOf((*RawMessage)(nil)).Elem(), encoder)
}
func (cfg *frozenConfig) useNumber() { func (cfg *frozenConfig) useNumber() {
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == NumberValue { if iter.WhatIsNext() == NumberValue {

View File

@ -64,7 +64,7 @@ func (iter *Iterator) trySkipString() bool {
} else if c == '\\' { } else if c == '\\' {
return false return false
} else if c < ' ' { } else if c < ' ' {
iter.ReportError("ReadString", iter.ReportError("trySkipString",
fmt.Sprintf(`invalid control character found: %d`, c)) fmt.Sprintf(`invalid control character found: %d`, c))
return true // already failed return true // already failed
} }

View File

@ -47,7 +47,7 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
str = append(str, c) str = append(str, c)
} }
} }
iter.ReportError("ReadString", "unexpected end of input") iter.ReportError("readStringSlowPath", "unexpected end of input")
return return
} }
@ -104,7 +104,7 @@ func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
case 't': case 't':
str = append(str, '\t') str = append(str, '\t')
default: default:
iter.ReportError("ReadString", iter.ReportError("readEscapedChar",
`invalid escape char after \`) `invalid escape char after \`)
return nil return nil
} }
@ -139,7 +139,7 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
} }
return copied return copied
} }
iter.ReportError("ReadString", `expects " or n`) iter.ReportError("ReadStringAsSlice", `expects " or n`)
return return
} }

View File

@ -154,11 +154,11 @@ func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool {
} }
func (encoder *placeholderEncoder) getRealEncoder() ValEncoder { func (encoder *placeholderEncoder) getRealEncoder() ValEncoder {
for i := 0; i < 30; i++ { for i := 0; i < 500; i++ {
realDecoder := encoder.cfg.getEncoderFromCache(encoder.cacheKey) realDecoder := encoder.cfg.getEncoderFromCache(encoder.cacheKey)
_, isPlaceholder := realDecoder.(*placeholderEncoder) _, isPlaceholder := realDecoder.(*placeholderEncoder)
if isPlaceholder { if isPlaceholder {
time.Sleep(time.Second) time.Sleep(10 * time.Millisecond)
} else { } else {
return realDecoder return realDecoder
} }
@ -172,11 +172,11 @@ type placeholderDecoder struct {
} }
func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
for i := 0; i < 30; i++ { for i := 0; i < 500; i++ {
realDecoder := decoder.cfg.getDecoderFromCache(decoder.cacheKey) realDecoder := decoder.cfg.getDecoderFromCache(decoder.cacheKey)
_, isPlaceholder := realDecoder.(*placeholderDecoder) _, isPlaceholder := realDecoder.(*placeholderDecoder)
if isPlaceholder { if isPlaceholder {
time.Sleep(time.Second) time.Sleep(10 * time.Millisecond)
} else { } else {
realDecoder.Decode(ptr, iter) realDecoder.Decode(ptr, iter)
return return
@ -466,6 +466,18 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
} }
return encoder, nil return encoder, nil
} }
if reflect.PtrTo(typ).Implements(marshalerType) {
checkIsEmpty, err := createCheckIsEmpty(reflect.PtrTo(typ))
if err != nil {
return nil, err
}
templateInterface := reflect.New(typ).Interface()
var encoder ValEncoder = &marshalerEncoder{
templateInterface: extractInterface(templateInterface),
checkIsEmpty: checkIsEmpty,
}
return encoder, nil
}
if typ.Implements(textMarshalerType) { if typ.Implements(textMarshalerType) {
checkIsEmpty, err := createCheckIsEmpty(typ) checkIsEmpty, err := createCheckIsEmpty(typ)
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
"encoding" "encoding"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"reflect"
"unsafe" "unsafe"
) )
@ -31,6 +32,10 @@ type intCodec struct {
} }
func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*int)(ptr)) = 0
return
}
*((*int)(ptr)) = iter.ReadInt() *((*int)(ptr)) = iter.ReadInt()
} }
@ -50,6 +55,10 @@ type uintptrCodec struct {
} }
func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uintptr)(ptr)) = 0
return
}
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64()) *((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
} }
@ -69,6 +78,10 @@ type int8Codec struct {
} }
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint8)(ptr)) = 0
return
}
*((*int8)(ptr)) = iter.ReadInt8() *((*int8)(ptr)) = iter.ReadInt8()
} }
@ -88,6 +101,10 @@ type int16Codec struct {
} }
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*int16)(ptr)) = 0
return
}
*((*int16)(ptr)) = iter.ReadInt16() *((*int16)(ptr)) = iter.ReadInt16()
} }
@ -107,6 +124,10 @@ type int32Codec struct {
} }
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*int32)(ptr)) = 0
return
}
*((*int32)(ptr)) = iter.ReadInt32() *((*int32)(ptr)) = iter.ReadInt32()
} }
@ -126,6 +147,10 @@ type int64Codec struct {
} }
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*int64)(ptr)) = 0
return
}
*((*int64)(ptr)) = iter.ReadInt64() *((*int64)(ptr)) = iter.ReadInt64()
} }
@ -145,6 +170,10 @@ type uintCodec struct {
} }
func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint)(ptr)) = 0
return
}
*((*uint)(ptr)) = iter.ReadUint() *((*uint)(ptr)) = iter.ReadUint()
} }
@ -164,6 +193,10 @@ type uint8Codec struct {
} }
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint8)(ptr)) = 0
return
}
*((*uint8)(ptr)) = iter.ReadUint8() *((*uint8)(ptr)) = iter.ReadUint8()
} }
@ -183,6 +216,10 @@ type uint16Codec struct {
} }
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint16)(ptr)) = 0
return
}
*((*uint16)(ptr)) = iter.ReadUint16() *((*uint16)(ptr)) = iter.ReadUint16()
} }
@ -202,6 +239,10 @@ type uint32Codec struct {
} }
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint32)(ptr)) = 0
return
}
*((*uint32)(ptr)) = iter.ReadUint32() *((*uint32)(ptr)) = iter.ReadUint32()
} }
@ -221,6 +262,10 @@ type uint64Codec struct {
} }
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*uint64)(ptr)) = 0
return
}
*((*uint64)(ptr)) = iter.ReadUint64() *((*uint64)(ptr)) = iter.ReadUint64()
} }
@ -240,6 +285,10 @@ type float32Codec struct {
} }
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*float32)(ptr)) = 0
return
}
*((*float32)(ptr)) = iter.ReadFloat32() *((*float32)(ptr)) = iter.ReadFloat32()
} }
@ -259,6 +308,10 @@ type float64Codec struct {
} }
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*float64)(ptr)) = 0
return
}
*((*float64)(ptr)) = iter.ReadFloat64() *((*float64)(ptr)) = iter.ReadFloat64()
} }
@ -278,8 +331,10 @@ type boolCodec struct {
} }
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if !iter.ReadNil() {
*((*bool)(ptr)) = iter.ReadBool() *((*bool)(ptr)) = iter.ReadBool()
} }
}
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteBool(*((*bool)(ptr))) stream.WriteBool(*((*bool)(ptr)))
@ -297,8 +352,17 @@ type emptyInterfaceCodec struct {
} }
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
*((*interface{})(ptr)) = nil
return
}
existing := *((*interface{})(ptr))
if existing != nil && reflect.TypeOf(existing).Kind() == reflect.Ptr {
iter.ReadVal(existing)
} else {
*((*interface{})(ptr)) = iter.Read() *((*interface{})(ptr)) = iter.Read()
} }
}
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteVal(*((*interface{})(ptr))) stream.WriteVal(*((*interface{})(ptr)))
@ -309,7 +373,8 @@ func (codec *emptyInterfaceCodec) EncodeInterface(val interface{}, stream *Strea
} }
func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool { func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
return ptr == nil emptyInterface := (*emptyInterface)(ptr)
return emptyInterface.typ == nil
} }
type nonEmptyInterfaceCodec struct { type nonEmptyInterfaceCodec struct {
@ -596,6 +661,7 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface.word = ptr templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface)) realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler) marshaler := (*realInterface).(json.Marshaler)
bytes, err := marshaler.MarshalJSON() bytes, err := marshaler.MarshalJSON()
if err != nil { if err != nil {
stream.Error = err stream.Error = err

View File

@ -82,3 +82,32 @@ func Test_decode_string_bool(t *testing.T) {
err = Unmarshal([]byte(`{"Field":true}`), &obj) err = Unmarshal([]byte(`{"Field":true}`), &obj)
should.NotNil(err) should.NotNil(err)
} }
func Test_bool_can_be_null(t *testing.T) {
type TestData struct {
Field bool `json:"field"`
}
should := require.New(t)
obj := TestData{}
data1 := []byte(`{"field": true}`)
err := Unmarshal(data1, &obj)
should.Equal(nil, err)
should.Equal(true, obj.Field)
data2 := []byte(`{"field": null}`)
err = Unmarshal(data2, &obj)
should.Equal(nil, err)
// Same behavior as stdlib, not touching the existing value.
should.Equal(true, obj.Field)
// Checking stdlib behavior as well
obj2 := TestData{}
err = json.Unmarshal(data1, &obj2)
should.Equal(nil, err)
should.Equal(true, obj2.Field)
err = json.Unmarshal(data2, &obj2)
should.Equal(nil, err)
should.Equal(true, obj2.Field)
}

View File

@ -314,3 +314,27 @@ func Test_recursive_empty_interface_customization(t *testing.T) {
Unmarshal([]byte("[100]"), &obj) Unmarshal([]byte("[100]"), &obj)
should.Equal([]interface{}{int64(100)}, obj) should.Equal([]interface{}{int64(100)}, obj)
} }
type GeoLocation struct {
Id string `json:"id,omitempty" db:"id"`
}
func (p *GeoLocation) MarshalJSON() ([]byte, error) {
return []byte(`{}`), nil
}
func (p *GeoLocation) UnmarshalJSON(input []byte) error {
p.Id = "hello"
return nil
}
func Test_marshal_and_unmarshal_on_non_pointer(t *testing.T) {
should := require.New(t)
locations := []GeoLocation{{"000"}}
bytes, err := Marshal(locations)
should.Nil(err)
should.Equal("[{}]", string(bytes))
err = Unmarshal([]byte("[1]"), &locations)
should.Nil(err)
should.Equal("hello", locations[0].Id)
}

View File

@ -0,0 +1,50 @@
package jsoniter
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
type MyEnum int64
const (
MyEnumA MyEnum = iota
MyEnumB
)
func (m *MyEnum) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"foo-%d"`, int(*m))), nil
}
func (m *MyEnum) UnmarshalJSON(jb []byte) error {
switch string(jb) {
case `"foo-1"`:
*m = MyEnumB
default:
*m = MyEnumA
}
return nil
}
func Test_custom_marshaler_on_enum(t *testing.T) {
type Wrapper struct {
Payload interface{}
}
type Wrapper2 struct {
Payload MyEnum
}
should := require.New(t)
w := Wrapper{Payload: MyEnumB}
jb, err := Marshal(w)
should.Equal(nil, err)
should.Equal(`{"Payload":"foo-1"}`, string(jb))
var w2 Wrapper2
err = Unmarshal(jb, &w2)
should.Equal(nil, err)
should.Equal(MyEnumB, w2.Payload)
}

View File

@ -20,7 +20,45 @@ func Test_read_uint64_invalid(t *testing.T) {
should.NotNil(iter.Error) should.NotNil(iter.Error)
} }
func Test_read_int8(t *testing.T) { func Test_read_int_from_null(t *testing.T) {
type TestObject struct {
F1 int8
F2 int16
F3 int32
F4 int64
F5 int
F6 uint8
F7 uint16
F8 uint32
F9 uint64
F10 uint
F11 float32
F12 float64
F13 uintptr
}
should := require.New(t)
obj := TestObject{}
err := Unmarshal([]byte(`{
"f1":null,
"f2":null,
"f3":null,
"f4":null,
"f5":null,
"f6":null,
"f7":null,
"f8":null,
"f9":null,
"f10":null,
"f11":null,
"f12":null,
"f13":null
}`), &obj)
should.Nil(err)
}
func _int8(t *testing.T) {
inputs := []string{`127`, `-128`} inputs := []string{`127`, `-128`}
for _, input := range inputs { for _, input := range inputs {
t.Run(fmt.Sprintf("%v", input), func(t *testing.T) { t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {

View File

@ -2,9 +2,11 @@ package jsoniter
import ( import (
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "fmt"
"testing" "testing"
"unsafe" "unsafe"
"github.com/stretchr/testify/require"
) )
func Test_write_array_of_interface(t *testing.T) { func Test_write_array_of_interface(t *testing.T) {
@ -297,3 +299,74 @@ func Test_array_with_nothing(t *testing.T) {
should.Nil(err) should.Nil(err)
should.Equal(`[null,null]`, output) should.Equal(`[null,null]`, 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 = 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"`
}
should := require.New(t)
var boolVar bool
obj := TestData{
Field: &boolVar,
}
data1 := []byte(`{"field": true}`)
err := Unmarshal(data1, &obj)
should.Equal(nil, err)
should.Equal(true, *(obj.Field.(*bool)))
data2 := []byte(`{"field": null}`)
err = Unmarshal(data2, &obj)
should.Equal(nil, err)
should.Equal(nil, obj.Field)
// Checking stdlib behavior matches.
obj2 := TestData{
Field: &boolVar,
}
err = json.Unmarshal(data1, &obj2)
should.Equal(nil, err)
should.Equal(true, *(obj2.Field.(*bool)))
err = json.Unmarshal(data2, &obj2)
should.Equal(nil, err)
should.Equal(nil, obj2.Field)
}
func Test_omitempty_nil_interface(t *testing.T) {
type TestData struct {
Field interface{} `json:"field,omitempty"`
}
should := require.New(t)
obj := TestData{
Field: nil,
}
js, err := json.Marshal(obj)
should.Equal(nil, err)
should.Equal("{}", string(js))
str, err := MarshalToString(obj)
should.Equal(nil, err)
should.Equal(string(js), str)
}

View File

@ -1,11 +1,11 @@
package jsoniter package jsoniter
import ( import (
"bytes"
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io" "io"
"testing" "testing"
"bytes"
) )
func Test_missing_object_end(t *testing.T) { func Test_missing_object_end(t *testing.T) {

View File

@ -72,3 +72,17 @@ func Test_encode_map_of_jsoniter_raw_message(t *testing.T) {
should.Nil(err) should.Nil(err)
should.Equal(`{"hello":[]}`, output) should.Equal(`{"hello":[]}`, output)
} }
func Test_marshal_invalid_json_raw_message(t *testing.T) {
type A struct {
Raw json.RawMessage `json:"raw"`
}
message := []byte(`{}`)
a := A{}
should := require.New(t)
should.Nil(ConfigCompatibleWithStandardLibrary.Unmarshal(message, &a))
aout, aouterr := ConfigCompatibleWithStandardLibrary.Marshal(&a)
should.Equal(`{"raw":null}`, string(aout))
should.Nil(aouterr)
}