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

56 Commits

Author SHA1 Message Date
6ed27152e0 Update README.md 2017-09-17 16:07:42 +08:00
3c298d8a76 Merge pull request #172 from olegshaldybin/more-stdlib-compat
Improve stdlib compatibility
2017-09-17 03:05:36 -05:00
9f6e5962a9 Improve stdlib compatibility
1. Null values for primitive types no longer clear the original value in
the destination object.

2. Dereference multiple levels of pointers in the destination interface{}
type before unmarshaling into it. This is needed to match stdlib
behavior when working with nested interface{} fields. If the destination
object is a pointer to interface{} then the incoming nil value should
nil out the destination object but keep the reference to that nil value
on its parent object. However if the destination object is an
interface{} value it should set the reference to nil but keep the
original object intact.

3. Correctly handle typed nil decode destinations.
2017-09-16 16:57:51 -07:00
c463aa12c4 Merge pull request #173 from toffaletti/more-nil-interface-fixes
More nil interface fixes
2017-09-16 18:36:43 -05:00
b5d2607a6d replace should.Equal(nil, err) with should.NoError(err) 2017-09-16 16:30:04 -07:00
48cc4d965a improve test 2017-09-16 16:27:32 -07:00
c59c42fda0 fix decoding of nil non-empty interface 2017-09-16 16:24:55 -07:00
8324374402 add tests for decoding nil interfaces 2017-09-16 16:24:27 -07:00
2017f3866b fix encoding of nil marshaler interface 2017-09-16 16:08:32 -07:00
ddc5af4512 fix encoding of nil non-empty interface 2017-09-16 16:04:36 -07:00
2f7e5c8dd7 add failing tests for nil non-empty interfaces 2017-09-16 16:00:48 -07:00
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
36b14963da #153 fix invalid utf8 using same implementation as the standard library 2017-08-29 23:58:51 +08:00
f706335302 #153 fix critical bug: infinite loop when write string is invalid utf8 2017-08-29 23:39:43 +08:00
2dc0031b26 #152 gofmt 2017-08-25 12:53:23 +08:00
cdbd2ed810 #145 interface {} customizatoin is recursive 2017-08-22 10:39:01 +08:00
39e9d67807 Merge branch 'master' of https://github.com/json-iterator/go 2017-08-22 00:12:18 +08:00
2066b01acb #146 support config TagKey 2017-08-22 00:12:09 +08:00
ac3b3cd160 test []interface{} 2017-08-21 22:43:51 +08:00
887789156a Merge pull request #147 from thockin/output_tests
Add tests for int64
2017-08-11 12:55:48 +08:00
7df5a67d0d Add tests for int64 2017-08-10 20:58:49 -07:00
9c358632dc #144 make []byte support Unmarshaler&Marshaler 2017-08-09 13:59:40 +08:00
1cfa233923 #143 make jsoniter.Number same meaning as json.Number, however UseNumber still returns json.Number. 1.9 alias support should be added later 2017-08-05 07:22:53 +08:00
d249b05a85 rename ValueType, to avoid collision with json.Number 2017-08-05 07:10:15 +08:00
abbd16da6c #140 blind fix 2017-08-02 09:20:43 +08:00
b67201557a avoid gc issue 2017-08-01 08:34:38 +08:00
5124683f24 #140 try fix: maybe memory collected before assigned to existing object graph 2017-07-31 23:24:58 +08:00
4892de725b add ad 2017-07-31 21:49:02 +08:00
34a2174be3 #142 decode struct field should be case insensitiveyet another fix 2017-07-31 21:48:22 +08:00
24ecaff2a1 #142 decode struct field should be case insensitive, the bug only happen for struct with more than 10 fields 2017-07-31 20:50:07 +08:00
c15b4d116c #139 unmarshal non base64 into []byte 2017-07-19 12:04:22 +08:00
12cd299fa8 add benchmark for Skip() 2017-07-19 00:22:41 +08:00
60ba332980 acknowledge @mattn for #138 #137 #136 #135 2017-07-19 00:09:50 +08:00
f705934fbf #138 fix - without following digits; fix 1.e1 2017-07-18 23:48:40 +08:00
17a26a6e20 remove debug print 2017-07-18 23:24:21 +08:00
156284b028 #137 fix unicode surrogate incompatibility 2017-07-18 23:17:52 +08:00
6b6938829d #136 strconv.ParseFloat can not validate 1. , added extra validation for this special case 2017-07-18 22:19:52 +08:00
60 changed files with 2168 additions and 548 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

@ -8,6 +8,10 @@
A high-performance 100% compatible drop-in replacement of "encoding/json" A high-performance 100% compatible drop-in replacement of "encoding/json"
```
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
```
# Benchmark # Benchmark
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
@ -70,6 +74,9 @@ go get github.com/json-iterator/go
Contributors Contributors
* [thockin](https://github.com/thockin) * [thockin](https://github.com/thockin)
* [mattn](https://github.com/mattn)
* [cch123](https://github.com/cch123) * [cch123](https://github.com/cch123)
* [Oleg Shaldybin](https://github.com/olegshaldybin)
* [Jason Toffaletti](https://github.com/toffaletti)
Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby)

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)
@ -158,7 +160,7 @@ type tolerateEmptyArrayDecoder struct {
} }
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
if iter.WhatIsNext() == jsoniter.Array { if iter.WhatIsNext() == jsoniter.ArrayValue {
iter.Skip() iter.Skip()
newIter := iter.Pool().BorrowIterator([]byte("{}")) newIter := iter.Pool().BorrowIterator([]byte("{}"))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
@ -174,11 +176,11 @@ type fuzzyStringDecoder struct {
func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
*((*string)(ptr)) = string(number) *((*string)(ptr)) = string(number)
case jsoniter.String: case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString() *((*string)(ptr)) = iter.ReadString()
default: default:
iter.ReportError("fuzzyStringDecoder", "not number or string") iter.ReportError("fuzzyStringDecoder", "not number or string")
@ -193,12 +195,18 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
var number json.Number var number json.Number
iter.ReadVal(&number) iter.ReadVal(&number)
str = string(number) str = string(number)
case jsoniter.String: 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
} }
} }
@ -218,16 +226,23 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float32)(ptr)) = iter.ReadFloat32() *((*float32)(ptr)) = iter.ReadFloat32()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
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")
} }
@ -240,16 +255,23 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
var str string var str string
switch valueType { switch valueType {
case jsoniter.Number: case jsoniter.NumberValue:
*((*float64)(ptr)) = iter.ReadFloat64() *((*float64)(ptr)) = iter.ReadFloat64()
case jsoniter.String: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
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

@ -90,7 +90,7 @@ func (adapter *Decoder) Buffered() io.Reader {
return bytes.NewReader(remaining) return bytes.NewReader(remaining)
} }
// UseNumber for number JSON element, use float64 or json.Number (alias of string) // UseNumber for number JSON element, use float64 or json.NumberValue (alias of string)
func (adapter *Decoder) UseNumber() { func (adapter *Decoder) UseNumber() {
origCfg := adapter.iter.cfg.configBeforeFrozen origCfg := adapter.iter.cfg.configBeforeFrozen
origCfg.UseNumber = true origCfg.UseNumber = true

View File

@ -13,7 +13,7 @@ type arrayLazyAny struct {
} }
func (any *arrayLazyAny) ValueType() ValueType { func (any *arrayLazyAny) ValueType() ValueType {
return Array return ArrayValue
} }
func (any *arrayLazyAny) MustBeValid() Any { func (any *arrayLazyAny) MustBeValid() Any {
@ -117,7 +117,7 @@ func (any *arrayLazyAny) Get(path ...interface{}) Any {
arr := make([]Any, 0) arr := make([]Any, 0)
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *Iterator) bool {
found := iter.readAny().Get(path[1:]...) found := iter.readAny().Get(path[1:]...)
if found.ValueType() != Invalid { if found.ValueType() != InvalidValue {
arr = append(arr, found) arr = append(arr, found)
} }
return true return true
@ -162,7 +162,7 @@ func wrapArray(val interface{}) *arrayAny {
} }
func (any *arrayAny) ValueType() ValueType { func (any *arrayAny) ValueType() ValueType {
return Array return ArrayValue
} }
func (any *arrayAny) MustBeValid() Any { func (any *arrayAny) MustBeValid() Any {
@ -253,7 +253,7 @@ func (any *arrayAny) Get(path ...interface{}) Any {
mappedAll := make([]Any, 0) mappedAll := make([]Any, 0)
for i := 0; i < any.val.Len(); i++ { for i := 0; i < any.val.Len(); i++ {
mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...) mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll = append(mappedAll, mapped) mappedAll = append(mappedAll, mapped)
} }
} }

View File

@ -61,7 +61,7 @@ func (any *trueAny) GetInterface() interface{} {
} }
func (any *trueAny) ValueType() ValueType { func (any *trueAny) ValueType() ValueType {
return Bool return BoolValue
} }
func (any *trueAny) MustBeValid() Any { func (any *trueAny) MustBeValid() Any {
@ -129,7 +129,7 @@ func (any *falseAny) GetInterface() interface{} {
} }
func (any *falseAny) ValueType() ValueType { func (any *falseAny) ValueType() ValueType {
return Bool return BoolValue
} }
func (any *falseAny) MustBeValid() Any { func (any *falseAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *floatAny) Parse() *Iterator {
} }
func (any *floatAny) ValueType() ValueType { func (any *floatAny) ValueType() ValueType {
return Number return NumberValue
} }
func (any *floatAny) MustBeValid() Any { func (any *floatAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *int32Any) LastError() error {
} }
func (any *int32Any) ValueType() ValueType { func (any *int32Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *int32Any) MustBeValid() Any { func (any *int32Any) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *int64Any) LastError() error {
} }
func (any *int64Any) ValueType() ValueType { func (any *int64Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *int64Any) MustBeValid() Any { func (any *int64Any) MustBeValid() Any {

View File

@ -16,7 +16,7 @@ func (any *invalidAny) LastError() error {
} }
func (any *invalidAny) ValueType() ValueType { func (any *invalidAny) ValueType() ValueType {
return Invalid return InvalidValue
} }
func (any *invalidAny) MustBeValid() Any { func (any *invalidAny) MustBeValid() Any {

View File

@ -9,7 +9,7 @@ func (any *nilAny) LastError() error {
} }
func (any *nilAny) ValueType() ValueType { func (any *nilAny) ValueType() ValueType {
return Nil return NilValue
} }
func (any *nilAny) MustBeValid() Any { func (any *nilAny) MustBeValid() Any {

View File

@ -10,7 +10,7 @@ type numberLazyAny struct {
} }
func (any *numberLazyAny) ValueType() ValueType { func (any *numberLazyAny) ValueType() ValueType {
return Number return NumberValue
} }
func (any *numberLazyAny) MustBeValid() Any { func (any *numberLazyAny) MustBeValid() Any {

View File

@ -13,7 +13,7 @@ type objectLazyAny struct {
} }
func (any *objectLazyAny) ValueType() ValueType { func (any *objectLazyAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *objectLazyAny) MustBeValid() Any { func (any *objectLazyAny) MustBeValid() Any {
@ -91,7 +91,7 @@ func (any *objectLazyAny) Get(path ...interface{}) Any {
defer any.cfg.ReturnIterator(iter) defer any.cfg.ReturnIterator(iter)
iter.ReadMapCB(func(iter *Iterator, field string) bool { iter.ReadMapCB(func(iter *Iterator, field string) bool {
mapped := locatePath(iter, path[1:]) mapped := locatePath(iter, path[1:])
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[field] = mapped mappedAll[field] = mapped
} }
return true return true
@ -149,7 +149,7 @@ func wrapStruct(val interface{}) *objectAny {
} }
func (any *objectAny) ValueType() ValueType { func (any *objectAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *objectAny) MustBeValid() Any { func (any *objectAny) MustBeValid() Any {
@ -224,7 +224,7 @@ func (any *objectAny) Get(path ...interface{}) Any {
field := any.val.Field(i) field := any.val.Field(i)
if field.CanInterface() { if field.CanInterface() {
mapped := Wrap(field.Interface()).Get(path[1:]...) mapped := Wrap(field.Interface()).Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[any.val.Type().Field(i).Name] = mapped mappedAll[any.val.Type().Field(i).Name] = mapped
} }
} }
@ -268,7 +268,7 @@ func wrapMap(val interface{}) *mapAny {
} }
func (any *mapAny) ValueType() ValueType { func (any *mapAny) ValueType() ValueType {
return Object return ObjectValue
} }
func (any *mapAny) MustBeValid() Any { func (any *mapAny) MustBeValid() Any {
@ -337,7 +337,7 @@ func (any *mapAny) Get(path ...interface{}) Any {
keyAsStr := key.String() keyAsStr := key.String()
element := Wrap(any.val.MapIndex(key).Interface()) element := Wrap(any.val.MapIndex(key).Interface())
mapped := element.Get(path[1:]...) mapped := element.Get(path[1:]...)
if mapped.ValueType() != Invalid { if mapped.ValueType() != InvalidValue {
mappedAll[keyAsStr] = mapped mappedAll[keyAsStr] = mapped
} }
} }

View File

@ -22,7 +22,7 @@ func (any *stringAny) Parse() *Iterator {
} }
func (any *stringAny) ValueType() ValueType { func (any *stringAny) ValueType() ValueType {
return String return StringValue
} }
func (any *stringAny) MustBeValid() Any { func (any *stringAny) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *uint32Any) LastError() error {
} }
func (any *uint32Any) ValueType() ValueType { func (any *uint32Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *uint32Any) MustBeValid() Any { func (any *uint32Any) MustBeValid() Any {

View File

@ -14,7 +14,7 @@ func (any *uint64Any) LastError() error {
} }
func (any *uint64Any) ValueType() ValueType { func (any *uint64Any) ValueType() ValueType {
return Number return NumberValue
} }
func (any *uint64Any) MustBeValid() Any { func (any *uint64Any) MustBeValid() Any {

View File

@ -17,6 +17,8 @@ type Config struct {
EscapeHTML bool EscapeHTML bool
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
TagKey string
ValidateJsonRawMessage bool
} }
type frozenConfig struct { type frozenConfig struct {
@ -54,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
@ -82,19 +85,47 @@ 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() == Number { if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) *((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else { } else {
*((*interface{})(ptr)) = iter.Read() *((*interface{})(ptr)) = iter.Read()
} }
}}) }})
} }
func (cfg *frozenConfig) getTagKey() string {
tagKey := cfg.configBeforeFrozen.TagKey
if tagKey == "" {
return "json"
}
return tagKey
}
func (cfg *frozenConfig) registerExtension(extension Extension) { func (cfg *frozenConfig) registerExtension(extension Extension) {
cfg.extensions = append(cfg.extensions, extension) cfg.extensions = append(cfg.extensions, extension)

View File

@ -10,20 +10,20 @@ import (
type ValueType int type ValueType int
const ( const (
// Invalid invalid JSON element // InvalidValue invalid JSON element
Invalid ValueType = iota InvalidValue ValueType = iota
// String JSON element "string" // StringValue JSON element "string"
String StringValue
// Number JSON element 100 or 0.10 // NumberValue JSON element 100 or 0.10
Number NumberValue
// Nil JSON element null // NilValue JSON element null
Nil NilValue
// Bool JSON element true or false // BoolValue JSON element true or false
Bool BoolValue
// Array JSON element [] // ArrayValue JSON element []
Array ArrayValue
// Object JSON element {} // ObjectValue JSON element {}
Object ObjectValue
) )
var hexDigits []byte var hexDigits []byte
@ -45,25 +45,25 @@ func init() {
} }
valueTypes = make([]ValueType, 256) valueTypes = make([]ValueType, 256)
for i := 0; i < len(valueTypes); i++ { for i := 0; i < len(valueTypes); i++ {
valueTypes[i] = Invalid valueTypes[i] = InvalidValue
} }
valueTypes['"'] = String valueTypes['"'] = StringValue
valueTypes['-'] = Number valueTypes['-'] = NumberValue
valueTypes['0'] = Number valueTypes['0'] = NumberValue
valueTypes['1'] = Number valueTypes['1'] = NumberValue
valueTypes['2'] = Number valueTypes['2'] = NumberValue
valueTypes['3'] = Number valueTypes['3'] = NumberValue
valueTypes['4'] = Number valueTypes['4'] = NumberValue
valueTypes['5'] = Number valueTypes['5'] = NumberValue
valueTypes['6'] = Number valueTypes['6'] = NumberValue
valueTypes['7'] = Number valueTypes['7'] = NumberValue
valueTypes['8'] = Number valueTypes['8'] = NumberValue
valueTypes['9'] = Number valueTypes['9'] = NumberValue
valueTypes['t'] = Bool valueTypes['t'] = BoolValue
valueTypes['f'] = Bool valueTypes['f'] = BoolValue
valueTypes['n'] = Nil valueTypes['n'] = NilValue
valueTypes['['] = Array valueTypes['['] = ArrayValue
valueTypes['{'] = Object valueTypes['{'] = ObjectValue
} }
// Iterator is a io.Reader like object, with JSON specific read functions. // Iterator is a io.Reader like object, with JSON specific read functions.
@ -270,29 +270,33 @@ func (iter *Iterator) unreadByte() {
func (iter *Iterator) Read() interface{} { func (iter *Iterator) Read() interface{} {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
switch valueType { switch valueType {
case String: case StringValue:
return iter.ReadString() return iter.ReadString()
case Number: case NumberValue:
if iter.cfg.configBeforeFrozen.UseNumber { if iter.cfg.configBeforeFrozen.UseNumber {
return json.Number(iter.readNumberAsString()) return json.Number(iter.readNumberAsString())
} }
return iter.ReadFloat64() return iter.ReadFloat64()
case Nil: case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l') iter.skipFourBytes('n', 'u', 'l', 'l')
return nil return nil
case Bool: case BoolValue:
return iter.ReadBool() return iter.ReadBool()
case Array: case ArrayValue:
arr := []interface{}{} arr := []interface{}{}
iter.ReadArrayCB(func(iter *Iterator) bool { iter.ReadArrayCB(func(iter *Iterator) bool {
arr = append(arr, iter.Read()) var elem interface{}
iter.ReadVal(&elem)
arr = append(arr, elem)
return true return true
}) })
return arr return arr
case Object: case ObjectValue:
obj := map[string]interface{}{} obj := map[string]interface{}{}
iter.ReadMapCB(func(Iter *Iterator, field string) bool { iter.ReadMapCB(func(Iter *Iterator, field string) bool {
obj[field] = iter.Read() var elem interface{}
iter.ReadVal(&elem)
obj[field] = elem
return true return true
}) })
return obj return obj

View File

@ -4,6 +4,7 @@ import (
"io" "io"
"math/big" "math/big"
"strconv" "strconv"
"strings"
"unsafe" "unsafe"
) )
@ -129,6 +130,9 @@ non_decimal_loop:
if c == '.' { if c == '.' {
i++ i++
decimalPlaces := 0 decimalPlaces := 0
if i == iter.tail {
return iter.readFloat32SlowPath()
}
for ; i < iter.tail; i++ { for ; i < iter.tail; i++ {
c = iter.buf[i] c = iter.buf[i]
ind := floatDigits[c] ind := floatDigits[c]
@ -189,12 +193,9 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
return return
} }
if len(str) == 0 { errMsg := validateFloat(str)
iter.ReportError("readFloat32SlowPath", "empty number") if errMsg != "" {
return iter.ReportError("readFloat32SlowPath", errMsg)
}
if str[0] == '-' {
iter.ReportError("readFloat32SlowPath", "-- is not valid")
return return
} }
val, err := strconv.ParseFloat(str, 32) val, err := strconv.ParseFloat(str, 32)
@ -270,6 +271,9 @@ non_decimal_loop:
if c == '.' { if c == '.' {
i++ i++
decimalPlaces := 0 decimalPlaces := 0
if i == iter.tail {
return iter.readFloat64SlowPath()
}
for ; i < iter.tail; i++ { for ; i < iter.tail; i++ {
c = iter.buf[i] c = iter.buf[i]
ind := floatDigits[c] ind := floatDigits[c]
@ -301,12 +305,9 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
if iter.Error != nil && iter.Error != io.EOF { if iter.Error != nil && iter.Error != io.EOF {
return return
} }
if len(str) == 0 { errMsg := validateFloat(str)
iter.ReportError("readFloat64SlowPath", "empty number") if errMsg != "" {
return iter.ReportError("readFloat64SlowPath", errMsg)
}
if str[0] == '-' {
iter.ReportError("readFloat64SlowPath", "-- is not valid")
return return
} }
val, err := strconv.ParseFloat(str, 64) val, err := strconv.ParseFloat(str, 64)
@ -316,3 +317,25 @@ func (iter *Iterator) readFloat64SlowPath() (ret float64) {
} }
return val return val
} }
func validateFloat(str string) string {
// strconv.ParseFloat is not validating `1.` or `1.e1`
if len(str) == 0 {
return "empty number"
}
if str[0] == '-' {
return "-- is not valid"
}
dotPos := strings.IndexByte(str, '.')
if dotPos != -1 {
if dotPos == len(str)-1 {
return "dot can not be last character"
}
switch str[dotPos+1] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
return "missing digit after dot"
}
}
return ""
}

View File

@ -14,7 +14,7 @@ func (iter *Iterator) ReadNil() (ret bool) {
return false return false
} }
// ReadBool reads a json object as Bool // ReadBool reads a json object as BoolValue
func (iter *Iterator) ReadBool() (ret bool) { func (iter *Iterator) ReadBool() (ret bool) {
c := iter.nextToken() c := iter.nextToken()
if c == 't' { if c == 't' {

View File

@ -21,12 +21,24 @@ func (iter *Iterator) trySkipNumber() bool {
if dotFound { if dotFound {
iter.ReportError("validateNumber", `more than one dot found in number`) iter.ReportError("validateNumber", `more than one dot found in number`)
return true // already failed return true // already failed
} else {
dotFound = true
} }
if i+1 == iter.tail {
return false
}
c = iter.buf[i+1]
switch c {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
iter.ReportError("validateNumber", `missing digit after dot`)
return true // already failed
}
dotFound = true
default: default:
switch c { switch c {
case ',', ']', '}', ' ', '\t', '\n', '\r': case ',', ']', '}', ' ', '\t', '\n', '\r':
if iter.head == i {
return false // if - without following digits
}
iter.head = i iter.head = i
return true // must be valid return true // must be valid
} }
@ -52,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

@ -42,34 +42,48 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
} }
if c == '\\' { if c == '\\' {
c = iter.readByte() c = iter.readByte()
str = iter.readEscapedChar(c, str)
} else {
str = append(str, c)
}
}
iter.ReportError("readStringSlowPath", "unexpected end of input")
return
}
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
switch c { switch c {
case 'u', 'U': case 'u':
r := iter.readU4() r := iter.readU4()
if utf16.IsSurrogate(r) { if utf16.IsSurrogate(r) {
c = iter.readByte() c = iter.readByte()
if iter.Error != nil { if iter.Error != nil {
return return nil
} }
if c != '\\' { if c != '\\' {
iter.ReportError("ReadString", iter.unreadByte()
`expects \u after utf16 surrogate, but \ not found`) str = appendRune(str, r)
return return str
} }
c = iter.readByte() c = iter.readByte()
if iter.Error != nil { if iter.Error != nil {
return return nil
} }
if c != 'u' && c != 'U' { if c != 'u' {
iter.ReportError("ReadString", str = appendRune(str, r)
`expects \u after utf16 surrogate, but \u not found`) return iter.readEscapedChar(c, str)
return
} }
r2 := iter.readU4() r2 := iter.readU4()
if iter.Error != nil { if iter.Error != nil {
return return nil
} }
combined := utf16.DecodeRune(r, r2) combined := utf16.DecodeRune(r, r2)
if combined == '\uFFFD' {
str = appendRune(str, r)
str = appendRune(str, r2)
} else {
str = appendRune(str, combined) str = appendRune(str, combined)
}
} else { } else {
str = appendRune(str, r) str = appendRune(str, r)
} }
@ -90,16 +104,11 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
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 return nil
} }
} else { return str
str = append(str, c)
}
}
iter.ReportError("ReadString", "unexpected end of input")
return
} }
// ReadStringAsSlice read string from iterator without copying into string form. // ReadStringAsSlice read string from iterator without copying into string form.
@ -130,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
} }

15
feature_json_number.go Normal file
View File

@ -0,0 +1,15 @@
package jsoniter
import "encoding/json"
type Number string
func CastJsonNumber(val interface{}) (string, bool) {
switch typedVal := val.(type) {
case json.Number:
return string(typedVal), true
case Number:
return string(typedVal), true
}
return "", false
}

View File

@ -51,6 +51,7 @@ func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
} }
var jsonNumberType reflect.Type var jsonNumberType reflect.Type
var jsoniterNumberType reflect.Type
var jsonRawMessageType reflect.Type var jsonRawMessageType reflect.Type
var jsoniterRawMessageType reflect.Type var jsoniterRawMessageType reflect.Type
var anyType reflect.Type var anyType reflect.Type
@ -61,6 +62,7 @@ var textUnmarshalerType reflect.Type
func init() { func init() {
jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem() jsonNumberType = reflect.TypeOf((*json.Number)(nil)).Elem()
jsoniterNumberType = reflect.TypeOf((*Number)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem() jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem() jsoniterRawMessageType = reflect.TypeOf((*RawMessage)(nil)).Elem()
anyType = reflect.TypeOf((*Any)(nil)).Elem() anyType = reflect.TypeOf((*Any)(nil)).Elem()
@ -152,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
} }
@ -170,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
@ -280,8 +282,8 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
if typ.AssignableTo(jsonNumberType) { if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil return &jsonNumberCodec{}, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { if typ.AssignableTo(jsoniterNumberType) {
return &base64Codec{}, nil return &jsoniterNumberCodec{}, nil
} }
if typ.Implements(unmarshalerType) { if typ.Implements(unmarshalerType) {
templateInterface := reflect.New(typ).Elem().Interface() templateInterface := reflect.New(typ).Elem().Interface()
@ -309,6 +311,13 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)} var decoder ValDecoder = &textUnmarshalerDecoder{extractInterface(templateInterface)}
return decoder, nil return decoder, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
sliceDecoder, err := prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ))
if err != nil {
return nil, err
}
return &base64Codec{sliceDecoder: sliceDecoder}, nil
}
if typ.Implements(anyType) { if typ.Implements(anyType) {
return &anyCodec{}, nil return &anyCodec{}, nil
} }
@ -439,8 +448,8 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
if typ.AssignableTo(jsonNumberType) { if typ.AssignableTo(jsonNumberType) {
return &jsonNumberCodec{}, nil return &jsonNumberCodec{}, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { if typ.AssignableTo(jsoniterNumberType) {
return &base64Codec{typ}, nil return &jsoniterNumberCodec{}, nil
} }
if typ.Implements(marshalerType) { if typ.Implements(marshalerType) {
checkIsEmpty, err := createCheckIsEmpty(typ) checkIsEmpty, err := createCheckIsEmpty(typ)
@ -457,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 {
@ -472,6 +493,9 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error
} }
return encoder, nil return encoder, nil
} }
if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
return &base64Codec{}, nil
}
if typ.Implements(anyType) { if typ.Implements(anyType) {
return &anyCodec{}, nil return &anyCodec{}, nil
} }

View File

@ -227,7 +227,7 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
bindings := []*Binding{} bindings := []*Binding{}
for i := 0; i < typ.NumField(); i++ { for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
tag := field.Tag.Get("json") tag := field.Tag.Get(cfg.getTagKey())
tagParts := strings.Split(tag, ",") tagParts := strings.Split(tag, ",")
if tag == "-" { if tag == "-" {
continue continue
@ -373,7 +373,7 @@ func (bindings sortableBindings) Swap(i, j int) {
func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) { func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) {
for _, binding := range structDescriptor.Fields { for _, binding := range structDescriptor.Fields {
shouldOmitEmpty := false shouldOmitEmpty := false
tagParts := strings.Split(binding.Field.Tag.Get("json"), ",") tagParts := strings.Split(binding.Field.Tag.Get(cfg.getTagKey()), ",")
for _, tagPart := range tagParts[1:] { for _, tagPart := range tagParts[1:] {
if tagPart == "omitempty" { if tagPart == "omitempty" {
shouldOmitEmpty = true shouldOmitEmpty = true

View File

@ -32,8 +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)) = iter.ReadInt() *((*int)(ptr)) = iter.ReadInt()
} }
}
func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt(*((*int)(ptr))) stream.WriteInt(*((*int)(ptr)))
@ -51,8 +53,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)) = uintptr(iter.ReadUint64()) *((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
} }
}
func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint64(uint64(*((*uintptr)(ptr)))) stream.WriteUint64(uint64(*((*uintptr)(ptr))))
@ -70,8 +74,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() {
*((*int8)(ptr)) = iter.ReadInt8() *((*int8)(ptr)) = iter.ReadInt8()
} }
}
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt8(*((*int8)(ptr))) stream.WriteInt8(*((*int8)(ptr)))
@ -89,8 +95,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)) = iter.ReadInt16() *((*int16)(ptr)) = iter.ReadInt16()
} }
}
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt16(*((*int16)(ptr))) stream.WriteInt16(*((*int16)(ptr)))
@ -108,8 +116,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)) = iter.ReadInt32() *((*int32)(ptr)) = iter.ReadInt32()
} }
}
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt32(*((*int32)(ptr))) stream.WriteInt32(*((*int32)(ptr)))
@ -127,8 +137,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)) = iter.ReadInt64() *((*int64)(ptr)) = iter.ReadInt64()
} }
}
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteInt64(*((*int64)(ptr))) stream.WriteInt64(*((*int64)(ptr)))
@ -146,7 +158,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)) = iter.ReadUint() *((*uint)(ptr)) = iter.ReadUint()
return
}
} }
func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -165,8 +180,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)) = iter.ReadUint8() *((*uint8)(ptr)) = iter.ReadUint8()
} }
}
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint8(*((*uint8)(ptr))) stream.WriteUint8(*((*uint8)(ptr)))
@ -184,8 +201,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)) = iter.ReadUint16() *((*uint16)(ptr)) = iter.ReadUint16()
} }
}
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint16(*((*uint16)(ptr))) stream.WriteUint16(*((*uint16)(ptr)))
@ -203,8 +222,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)) = iter.ReadUint32() *((*uint32)(ptr)) = iter.ReadUint32()
} }
}
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint32(*((*uint32)(ptr))) stream.WriteUint32(*((*uint32)(ptr)))
@ -222,8 +243,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)) = iter.ReadUint64() *((*uint64)(ptr)) = iter.ReadUint64()
} }
}
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteUint64(*((*uint64)(ptr))) stream.WriteUint64(*((*uint64)(ptr)))
@ -241,8 +264,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)) = iter.ReadFloat32() *((*float32)(ptr)) = iter.ReadFloat32()
} }
}
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat32(*((*float32)(ptr))) stream.WriteFloat32(*((*float32)(ptr)))
@ -260,8 +285,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)) = iter.ReadFloat64() *((*float64)(ptr)) = iter.ReadFloat64()
} }
}
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteFloat64(*((*float64)(ptr))) stream.WriteFloat64(*((*float64)(ptr)))
@ -279,8 +306,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)))
@ -298,8 +327,43 @@ type emptyInterfaceCodec struct {
} }
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { 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() *((*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)))
@ -310,7 +374,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 {
@ -327,15 +392,20 @@ func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator)
e.typ = nonEmptyInterface.itab.typ e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word e.word = nonEmptyInterface.word
iter.ReadVal(&i) iter.ReadVal(&i)
if e.word == nil {
nonEmptyInterface.itab = nil
}
nonEmptyInterface.word = e.word nonEmptyInterface.word = e.word
} }
func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
nonEmptyInterface := (*nonEmptyInterface)(ptr) nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{} var i interface{}
if nonEmptyInterface.itab != nil {
e := (*emptyInterface)(unsafe.Pointer(&i)) e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word e.word = nonEmptyInterface.word
}
stream.WriteVal(i) stream.WriteVal(i)
} }
@ -386,6 +456,25 @@ func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*json.Number)(ptr))) == 0 return len(*((*json.Number)(ptr))) == 0
} }
type jsoniterNumberCodec struct {
}
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
}
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteRaw(string(*((*Number)(ptr))))
}
func (codec *jsoniterNumberCodec) EncodeInterface(val interface{}, stream *Stream) {
stream.WriteRaw(string(val.(Number)))
}
func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*Number)(ptr))) == 0
}
type jsonRawMessageCodec struct { type jsonRawMessageCodec struct {
} }
@ -425,7 +514,7 @@ func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
} }
type base64Codec struct { type base64Codec struct {
actualType reflect.Type sliceDecoder ValDecoder
} }
func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
@ -436,6 +525,8 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
ptrSlice.Data = nil ptrSlice.Data = nil
return return
} }
switch iter.WhatIsNext() {
case StringValue:
encoding := base64.StdEncoding encoding := base64.StdEncoding
src := iter.SkipAndReturnBytes() src := iter.SkipAndReturnBytes()
src = src[1 : len(src)-1] src = src[1 : len(src)-1]
@ -452,6 +543,11 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
ptrSlice.Cap = dstSlice.Cap ptrSlice.Cap = dstSlice.Cap
ptrSlice.Len = dstSlice.Len ptrSlice.Len = dstSlice.Len
} }
case ArrayValue:
codec.sliceDecoder.Decode(ptr, iter)
default:
iter.ReportError("base64Codec", "invalid input")
}
} }
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -570,7 +666,12 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
templateInterface := encoder.templateInterface templateInterface := encoder.templateInterface
templateInterface.word = ptr templateInterface.word = ptr
realInterface := (*interface{})(unsafe.Pointer(&templateInterface)) realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
marshaler := (*realInterface).(json.Marshaler) marshaler, ok := (*realInterface).(json.Marshaler)
if !ok {
stream.WriteVal(nil)
return
}
bytes, err := marshaler.MarshalJSON() bytes, err := marshaler.MarshalJSON()
if err != nil { if err != nil {
stream.Error = err stream.Error = err

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unsafe" "unsafe"
) )
@ -28,7 +29,7 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
if old.toName != toName { if old.toName != toName {
continue continue
} }
old.ignored, new.ignored = resolveConflictBinding(old.binding, new.binding) old.ignored, new.ignored = resolveConflictBinding(cfg, old.binding, new.binding)
} }
orderedBindings = append(orderedBindings, new) orderedBindings = append(orderedBindings, new)
} }
@ -48,9 +49,9 @@ func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil return &structEncoder{structDescriptor.onePtrEmbedded, structDescriptor.onePtrOptimization, finalOrderedFields}, nil
} }
func resolveConflictBinding(old, new *Binding) (ignoreOld, ignoreNew bool) { func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
newTagged := new.Field.Tag.Get("json") != "" newTagged := new.Field.Tag.Get(cfg.getTagKey()) != ""
oldTagged := old.Field.Tag.Get("json") != "" oldTagged := old.Field.Tag.Get(cfg.getTagKey()) != ""
if newTagged { if newTagged {
if oldTagged { if oldTagged {
if len(old.levels) > len(new.levels) { if len(old.levels) > len(new.levels) {
@ -90,7 +91,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
bindings[fromName] = binding bindings[fromName] = binding
continue continue
} }
ignoreOld, ignoreNew := resolveConflictBinding(old, binding) ignoreOld, ignoreNew := resolveConflictBinding(cfg, old, binding)
if ignoreOld { if ignoreOld {
delete(bindings, fromName) delete(bindings, fromName)
} }
@ -101,7 +102,7 @@ func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
} }
fields := map[string]*structFieldDecoder{} fields := map[string]*structFieldDecoder{}
for k, binding := range bindings { for k, binding := range bindings {
fields[k] = binding.Decoder.(*structFieldDecoder) fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
} }
return createStructDecoder(typ, fields) return createStructDecoder(typ, fields)
} }

View File

@ -124,7 +124,8 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
} }
} }
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer()) newVal := reflect.MakeSlice(sliceType, newLen, newCap)
dst := unsafe.Pointer(newVal.Pointer())
// copy old array into new array // copy old array into new array
originalBytesCount := uintptr(slice.Len) * elementType.Size() originalBytesCount := uintptr(slice.Len) * elementType.Size()
srcPtr := (*[1 << 30]byte)(slice.Data) srcPtr := (*[1 << 30]byte)(slice.Data)
@ -132,16 +133,17 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
for i := uintptr(0); i < originalBytesCount; i++ { for i := uintptr(0); i < originalBytesCount; i++ {
dstPtr[i] = srcPtr[i] dstPtr[i] = srcPtr[i]
} }
slice.Data = dst
slice.Len = newLen slice.Len = newLen
slice.Cap = newCap slice.Cap = newCap
slice.Data = dst
} }
func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) { func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) {
if expectedCap <= slice.Cap { if expectedCap <= slice.Cap {
return return
} }
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer()) newVal := reflect.MakeSlice(sliceType, 0, expectedCap)
slice.Cap = expectedCap dst := unsafe.Pointer(newVal.Pointer())
slice.Data = dst slice.Data = dst
slice.Cap = expectedCap
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"
"unsafe" "unsafe"
) )
@ -428,7 +429,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
} }
fieldBytes := iter.readObjectFieldAsBytes() fieldBytes := iter.readObjectFieldAsBytes()
field := *(*string)(unsafe.Pointer(&fieldBytes)) field := *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder := decoder.fields[field] fieldDecoder := decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()
} else { } else {
@ -437,7 +438,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
for iter.nextToken() == ',' { for iter.nextToken() == ',' {
fieldBytes = iter.readObjectFieldAsBytes() fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes)) field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field] fieldDecoder = decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()
} else { } else {
@ -455,7 +456,7 @@ type skipObjectDecoder struct {
func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
valueType := iter.WhatIsNext() valueType := iter.WhatIsNext()
if valueType != Object && valueType != Nil { if valueType != ObjectValue && valueType != NilValue {
iter.ReportError("skipObjectDecoder", "expect object or null") iter.ReportError("skipObjectDecoder", "expect object or null")
return return
} }

View File

@ -289,6 +289,8 @@ func writeStringSlowPathWithHTMLEscaped(stream *Stream, i int, s string, valLen
if start < i { if start < i {
stream.WriteRaw(s[start:i]) stream.WriteRaw(s[start:i])
} }
stream.WriteRaw(`\ufffd`)
i++
start = i start = i
continue continue
} }

View File

@ -9,10 +9,10 @@ import (
func Test_read_empty_array_as_any(t *testing.T) { func Test_read_empty_array_as_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Array, any.Get().ValueType()) should.Equal(ArrayValue, any.Get().ValueType())
should.Equal(Invalid, any.Get(0.3).ValueType()) should.Equal(InvalidValue, any.Get(0.3).ValueType())
should.Equal(0, any.Size()) should.Equal(0, any.Size())
should.Equal(Array, any.ValueType()) should.Equal(ArrayValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(int32(0), any.ToInt32()) should.Equal(int32(0), any.ToInt32())
@ -101,7 +101,7 @@ func Test_array_wrapper_any_get_all(t *testing.T) {
{5, 6}, {5, 6},
}) })
should.Equal("[1,3,5]", any.Get('*', 0).ToString()) should.Equal("[1,3,5]", any.Get('*', 0).ToString())
should.Equal(Array, any.ValueType()) should.Equal(ArrayValue, any.ValueType())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(1, any.Get(0, 0).ToInt()) should.Equal(1, any.Get(0, 0).ToInt())
} }
@ -109,14 +109,14 @@ func Test_array_wrapper_any_get_all(t *testing.T) {
func Test_array_lazy_any_get_invalid(t *testing.T) { func Test_array_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Invalid, any.Get(1, 1).ValueType()) should.Equal(InvalidValue, any.Get(1, 1).ValueType())
should.NotNil(any.Get(1, 1).LastError()) should.NotNil(any.Get(1, 1).LastError())
should.Equal(Invalid, any.Get("1").ValueType()) should.Equal(InvalidValue, any.Get("1").ValueType())
should.NotNil(any.Get("1").LastError()) should.NotNil(any.Get("1").LastError())
} }
func Test_invalid_array(t *testing.T) { func Test_invalid_array(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("["), 0) any := Get([]byte("["), 0)
should.Equal(Invalid, any.ValueType()) should.Equal(InvalidValue, any.ValueType())
} }

View File

@ -53,12 +53,12 @@ func Test_write_bool_to_stream(t *testing.T) {
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("true", string(stream.Buffer())) should.Equal("true", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
any = Get([]byte("false")) any = Get([]byte("false"))
stream = NewStream(ConfigDefault, nil, 32) stream = NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("false", string(stream.Buffer())) should.Equal("false", string(stream.Buffer()))
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
} }

View File

@ -96,7 +96,7 @@ func Test_read_float_to_any(t *testing.T) {
should.Equal(uint(0), any2.ToUint()) should.Equal(uint(0), any2.ToUint())
should.Equal(uint32(0), any2.ToUint32()) should.Equal(uint32(0), any2.ToUint32())
should.Equal(uint64(0), any2.ToUint64()) should.Equal(uint64(0), any2.ToUint64())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal("1.23E+01", any.ToString()) should.Equal("1.23E+01", any.ToString())
} }

View File

@ -123,7 +123,7 @@ func Test_read_int64_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -141,7 +141,7 @@ func Test_read_int32_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -160,7 +160,7 @@ func Test_read_uint32_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -179,7 +179,7 @@ func Test_read_uint64_to_any(t *testing.T) {
should.Equal(float64(12345), any.ToFloat64()) should.Equal(float64(12345), any.ToFloat64())
should.Equal("12345", any.ToString()) should.Equal("12345", any.ToString())
should.Equal(true, any.ToBool()) should.Equal(true, any.ToBool())
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
stream := NewStream(ConfigDefault, nil, 32) stream := NewStream(ConfigDefault, nil, 32)
any.WriteTo(stream) any.WriteTo(stream)
should.Equal("12345", string(stream.Buffer())) should.Equal("12345", string(stream.Buffer()))
@ -193,5 +193,5 @@ func Test_int_lazy_any_get(t *testing.T) {
any := Get([]byte("1234")) any := Get([]byte("1234"))
// panic!! // panic!!
//should.Equal(any.LastError(), io.EOF) //should.Equal(any.LastError(), io.EOF)
should.Equal(Invalid, any.Get(1, "2").ValueType()) should.Equal(InvalidValue, any.Get(1, "2").ValueType())
} }

View File

@ -20,7 +20,7 @@ func Test_read_object_as_any(t *testing.T) {
should.Equal(2, any.Size()) should.Equal(2, any.Size())
should.True(any.ToBool()) should.True(any.ToBool())
should.Equal(0, any.ToInt()) should.Equal(0, any.ToInt())
should.Equal(Object, any.ValueType()) should.Equal(ObjectValue, any.ValueType())
should.Nil(any.LastError()) should.Nil(any.LastError())
obj := struct { obj := struct {
A string A string
@ -44,8 +44,8 @@ func Test_object_lazy_any_get_all(t *testing.T) {
func Test_object_lazy_any_get_invalid(t *testing.T) { func Test_object_lazy_any_get_invalid(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte(`{}`)) any := Get([]byte(`{}`))
should.Equal(Invalid, any.Get("a", "stream", "c").ValueType()) should.Equal(InvalidValue, any.Get("a", "stream", "c").ValueType())
should.Equal(Invalid, any.Get(1).ValueType()) should.Equal(InvalidValue, any.Get(1).ValueType())
} }
func Test_wrap_map_and_convert_to_any(t *testing.T) { func Test_wrap_map_and_convert_to_any(t *testing.T) {

View File

@ -156,7 +156,7 @@ func Test_encode_byte_array(t *testing.T) {
should.Equal(`"AQID"`, string(bytes)) should.Equal(`"AQID"`, string(bytes))
} }
func Test_decode_byte_array(t *testing.T) { func Test_decode_byte_array_from_base64(t *testing.T) {
should := require.New(t) should := require.New(t)
data := []byte{} data := []byte{}
err := json.Unmarshal([]byte(`"AQID"`), &data) err := json.Unmarshal([]byte(`"AQID"`), &data)
@ -167,6 +167,17 @@ func Test_decode_byte_array(t *testing.T) {
should.Equal([]byte{1, 2, 3}, data) should.Equal([]byte{1, 2, 3}, data)
} }
func Test_decode_byte_array_from_array(t *testing.T) {
should := require.New(t)
data := []byte{}
err := json.Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
err = Unmarshal([]byte(`[1,2,3]`), &data)
should.Nil(err)
should.Equal([]byte{1, 2, 3}, data)
}
func Test_decode_slice(t *testing.T) { func Test_decode_slice(t *testing.T) {
should := require.New(t) should := require.New(t)
slice := make([]string, 0, 5) slice := make([]string, 0, 5)

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.NoError(err)
should.Equal(true, obj.Field)
data2 := []byte(`{"field": null}`)
err = Unmarshal(data2, &obj)
should.NoError(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.NoError(err)
should.Equal(true, obj2.Field)
err = json.Unmarshal(data2, &obj2)
should.NoError(err)
should.Equal(true, obj2.Field)
}

View File

@ -285,3 +285,56 @@ func Test_marshal_json_with_time(t *testing.T) {
should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj)) should.Nil(Unmarshal([]byte(`{"TF1":{"F1":"fake","F2":"fake"}}`), &obj))
should.NotNil(obj.TF1.F2) should.NotNil(obj.TF1.F2)
} }
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {
Field string `orm:"field"`
}
should := require.New(t)
json := Config{TagKey: "orm"}.Froze()
str, err := json.MarshalToString(TestObject{"hello"})
should.Nil(err)
should.Equal(`{"field":"hello"}`, str)
}
func Test_recursive_empty_interface_customization(t *testing.T) {
t.Skip()
var obj interface{}
RegisterTypeDecoderFunc("interface {}", func(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case NumberValue:
*(*interface{})(ptr) = iter.ReadInt64()
default:
*(*interface{})(ptr) = iter.Read()
}
})
should := require.New(t)
Unmarshal([]byte("[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.NoError(err)
should.Equal(`{"Payload":"foo-1"}`, string(jb))
var w2 Wrapper2
err = Unmarshal(jb, &w2)
should.NoError(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) {
@ -457,6 +495,17 @@ func Test_json_number(t *testing.T) {
should.Equal(`[1]`, str) should.Equal(`[1]`, str)
} }
func Test_jsoniter_number(t *testing.T) {
should := require.New(t)
var arr []Number
err := Unmarshal([]byte(`[1]`), &arr)
should.Nil(err)
should.Equal(Number("1"), arr[0])
str, isNumber := CastJsonNumber(arr[0])
should.True(isNumber)
should.Equal("1", str)
}
func Benchmark_jsoniter_encode_int(b *testing.B) { func Benchmark_jsoniter_encode_int(b *testing.B) {
stream := NewStream(ConfigDefault, ioutil.Discard, 64) stream := NewStream(ConfigDefault, ioutil.Discard, 64)
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {

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,262 @@ 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.NoError(err)
should.Equal(true, *(obj.Field.(*bool)))
data2 := []byte(`{"field": null}`)
err = Unmarshal(data2, &obj)
should.NoError(err)
should.Equal(nil, obj.Field)
// Checking stdlib behavior matches.
obj2 := TestData{
Field: &boolVar,
}
err = json.Unmarshal(data1, &obj2)
should.NoError(err)
should.Equal(true, *(obj2.Field.(*bool)))
err = json.Unmarshal(data2, &obj2)
should.NoError(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.NoError(err)
should.Equal("{}", string(js))
str, err := MarshalToString(obj)
should.NoError(err)
should.Equal(string(js), str)
}
func Test_omitempty_nil_nonempty_interface(t *testing.T) {
type TestData struct {
Field MyInterface `json:"field,omitempty"`
}
should := require.New(t)
obj := TestData{
Field: nil,
}
js, err := json.Marshal(obj)
should.NoError(err)
should.Equal("{}", string(js))
str, err := MarshalToString(obj)
should.NoError(err)
should.Equal(string(js), str)
obj.Field = MyString("hello")
err = UnmarshalFromString(`{"field":null}`, &obj)
should.NoError(err)
should.Equal(nil, obj.Field)
}
func Test_marshal_nil_marshaler_interface(t *testing.T) {
type TestData struct {
Field json.Marshaler `json:"field"`
}
should := require.New(t)
obj := TestData{
Field: nil,
}
js, err := json.Marshal(obj)
should.NoError(err)
should.Equal(`{"field":null}`, string(js))
str, err := MarshalToString(obj)
should.NoError(err)
should.Equal(string(js), str)
}
func Test_marshal_nil_nonempty_interface(t *testing.T) {
type TestData struct {
Field MyInterface `json:"field"`
}
should := require.New(t)
obj := TestData{
Field: nil,
}
js, err := json.Marshal(obj)
should.NoError(err)
should.Equal(`{"field":null}`, string(js))
str, err := MarshalToString(obj)
should.NoError(err)
should.Equal(string(js), str)
obj.Field = MyString("hello")
err = Unmarshal(js, &obj)
should.NoError(err)
should.Equal(nil, obj.Field)
}
func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
type Payload struct {
Value int `json:"val,omitempty"`
}
should := require.New(t)
payload := &Payload{}
wrapper := &Wrapper{
Payload: &payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, 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.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
payload = &Payload{}
wrapper = &Wrapper{
Payload: &payload,
}
err = Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.Equal(&payload, wrapper.Payload)
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
}
func Test_overwrite_interface_value_with_nil(t *testing.T) {
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
type Payload struct {
Value int `json:"val,omitempty"`
}
should := require.New(t)
payload := &Payload{}
wrapper := &Wrapper{
Payload: payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.Equal(nil, wrapper.Payload)
should.Equal(42, payload.Value)
payload = &Payload{}
wrapper = &Wrapper{
Payload: payload,
}
err = Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
err = Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.Equal(nil, wrapper.Payload)
should.Equal(42, payload.Value)
}
func Test_unmarshal_into_nil(t *testing.T) {
type Payload struct {
Value int `json:"val,omitempty"`
}
type Wrapper struct {
Payload interface{} `json:"payload,omitempty"`
}
should := require.New(t)
var payload *Payload
wrapper := &Wrapper{
Payload: payload,
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Nil(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
payload = nil
wrapper = &Wrapper{
Payload: payload,
}
err = Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Nil(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
}

View File

@ -1,6 +1,7 @@
package jsoniter package jsoniter
import ( import (
"bytes"
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io" "io"
@ -25,7 +26,7 @@ func Test_missing_array_end(t *testing.T) {
func Test_invalid_any(t *testing.T) { func Test_invalid_any(t *testing.T) {
should := require.New(t) should := require.New(t)
any := Get([]byte("[]")) any := Get([]byte("[]"))
should.Equal(Invalid, any.Get(0.3).ValueType()) should.Equal(InvalidValue, any.Get(0.3).ValueType())
// is nil correct ? // is nil correct ?
should.Equal(nil, any.Get(0.3).GetInterface()) should.Equal(nil, any.Get(0.3).GetInterface())
@ -41,7 +42,7 @@ func Test_invalid_any(t *testing.T) {
should.Equal(float64(0), any.ToFloat64()) should.Equal(float64(0), any.ToFloat64())
should.Equal("", any.ToString()) should.Equal("", any.ToString())
should.Equal(Invalid, any.Get(0.1).Get(1).ValueType()) should.Equal(InvalidValue, any.Get(0.1).Get(1).ValueType())
} }
func Test_invalid_struct_input(t *testing.T) { func Test_invalid_struct_input(t *testing.T) {
@ -68,46 +69,64 @@ func Test_invalid_array_input(t *testing.T) {
should.NotNil(Unmarshal(input, &obj)) should.NotNil(Unmarshal(input, &obj))
} }
func Test_double_negative(t *testing.T) { func Test_invalid_float(t *testing.T) {
inputs := []string{
`1.e1`, // dot without following digit
`1.`, // dot can not be the last char
``, // empty number
`01`, // extra leading zero
`-`, // negative without digit
`--`, // double negative
`--2`, // double negative
}
for _, input := range inputs {
t.Run(input, func(t *testing.T) {
should := require.New(t) should := require.New(t)
var v interface{} iter := ParseString(ConfigDefault, input+",")
should.NotNil(json.Unmarshal([]byte(`--2`), &v))
var vFloat64 float64
should.NotNil(UnmarshalFromString(`--2`, &vFloat64))
var vFloat32 float32
should.NotNil(UnmarshalFromString(`--2`, &vFloat32))
var vInt int
should.NotNil(UnmarshalFromString(`--2`, &vInt))
iter := ParseString(ConfigDefault, `--2`)
iter.Skip() iter.Skip()
should.NotEqual(io.EOF, iter.Error) should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error) should.NotNil(iter.Error)
} v := float64(0)
should.NotNil(json.Unmarshal([]byte(input), &v))
func Test_leading_zero(t *testing.T) { iter = ParseString(ConfigDefault, input+",")
should := require.New(t)
var v interface{}
should.NotNil(json.Unmarshal([]byte(`01`), &v))
var vFloat64 float64
should.NotNil(UnmarshalFromString(`01`, &vFloat64))
var vFloat32 float32
should.NotNil(UnmarshalFromString(`01`, &vFloat32))
var vInt int
should.NotNil(UnmarshalFromString(`01`, &vInt))
iter := ParseString(ConfigDefault, `01,`)
iter.Skip()
should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error)
}
func Test_empty_as_number(t *testing.T) {
should := require.New(t)
iter := ParseString(ConfigDefault, `,`)
iter.ReadFloat64() iter.ReadFloat64()
should.NotEqual(io.EOF, iter.Error) should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error) should.NotNil(iter.Error)
iter = ParseString(ConfigDefault, `,`) iter = ParseString(ConfigDefault, input+",")
iter.ReadFloat32() iter.ReadFloat32()
should.NotEqual(io.EOF, iter.Error) should.NotEqual(io.EOF, iter.Error)
should.NotNil(iter.Error) should.NotNil(iter.Error)
})
}
}
func Test_chan(t *testing.T) {
t.Skip("do not support chan")
type TestObject struct {
MyChan chan bool
MyField int
}
should := require.New(t)
obj := TestObject{}
str, err := json.Marshal(obj)
should.Nil(err)
should.Equal(``, str)
}
func Test_invalid_number(t *testing.T) {
type Message struct {
Number int `json:"number"`
}
obj := Message{}
decoder := ConfigCompatibleWithStandardLibrary.NewDecoder(bytes.NewBufferString(`{"number":"5"}`))
err := decoder.Decode(&obj)
invalidStr := err.Error()
result, err := ConfigCompatibleWithStandardLibrary.Marshal(invalidStr)
should := require.New(t)
should.Nil(err)
result2, err := json.Marshal(invalidStr)
should.Nil(err)
should.Equal(string(result2), string(result))
} }

View File

@ -23,17 +23,122 @@ import (
// } // }
//} //}
func init() {
ioutil.WriteFile("/tmp/large-file.json", []byte(`[{
"person": {
"id": "d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"name": {
"fullName": "Leonid Bugaev",
"givenName": "Leonid",
"familyName": "Bugaev"
},
"email": "leonsbox@gmail.com",
"gender": "male",
"location": "Saint Petersburg, Saint Petersburg, RU",
"geo": {
"city": "Saint Petersburg",
"state": "Saint Petersburg",
"country": "Russia",
"lat": 59.9342802,
"lng": 30.3350986
},
"bio": "Senior engineer at Granify.com",
"site": "http://flickfaver.com",
"avatar": "https://d1ts43dypk8bqh.cloudfront.net/v1/avatars/d50887ca-a6ce-4e59-b89f-14f0b5d03b03",
"employment": {
"name": "www.latera.ru",
"title": "Software Engineer",
"domain": "gmail.com"
},
"facebook": {
"handle": "leonid.bugaev"
},
"github": {
"handle": "buger",
"id": 14009,
"avatar": "https://avatars.githubusercontent.com/u/14009?v=3",
"company": "Granify",
"blog": "http://leonsbox.com",
"followers": 95,
"following": 10
},
"twitter": {
"handle": "flickfaver",
"id": 77004410,
"bio": null,
"followers": 2,
"following": 1,
"statuses": 5,
"favorites": 0,
"location": "",
"site": "http://flickfaver.com",
"avatar": null
},
"linkedin": {
"handle": "in/leonidbugaev"
},
"googleplus": {
"handle": null
},
"angellist": {
"handle": "leonid-bugaev",
"id": 61541,
"bio": "Senior engineer at Granify.com",
"blog": "http://buger.github.com",
"site": "http://buger.github.com",
"followers": 41,
"avatar": "https://d1qb2nb5cznatu.cloudfront.net/users/61541-medium_jpg?1405474390"
},
"klout": {
"handle": null,
"score": null
},
"foursquare": {
"handle": null
},
"aboutme": {
"handle": "leonid.bugaev",
"bio": null,
"avatar": null
},
"gravatar": {
"handle": "buger",
"urls": [
],
"avatar": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"avatars": [
{
"url": "http://1.gravatar.com/avatar/f7c8edd577d13b8930d5522f28123510",
"type": "thumbnail"
}
]
},
"fuzzy": false
},
"company": "hello"
}]`), 0666)
}
/*
200000 8886 ns/op 4336 B/op 6 allocs/op
50000 34244 ns/op 6744 B/op 14 allocs/op
*/
func Benchmark_jsoniter_large_file(b *testing.B) { func Benchmark_jsoniter_large_file(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json") file, _ := os.Open("/tmp/large-file.json")
iter := Parse(ConfigDefault, file, 4096) iter := Parse(ConfigDefault, file, 4096)
count := 0 count := 0
for iter.ReadArray() { iter.ReadArrayCB(func(iter *Iterator) bool {
// Skip() is strict by default, use --tags jsoniter-sloppy to skip without validation
iter.Skip() iter.Skip()
count++ count++
} return true
})
file.Close() file.Close()
if iter.Error != nil {
b.Error(iter.Error)
}
} }
} }
@ -44,6 +149,9 @@ func Benchmark_json_large_file(b *testing.B) {
bytes, _ := ioutil.ReadAll(file) bytes, _ := ioutil.ReadAll(file)
file.Close() file.Close()
result := []struct{}{} result := []struct{}{}
json.Unmarshal(bytes, &result) err := json.Unmarshal(bytes, &result)
if err != nil {
b.Error(err)
}
} }
} }

View File

@ -3,9 +3,10 @@ package jsoniter
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require"
"io" "io"
"testing" "testing"
"github.com/stretchr/testify/require"
) )
func Test_read_null(t *testing.T) { func Test_read_null(t *testing.T) {
@ -135,3 +136,33 @@ func Test_encode_nil_array(t *testing.T) {
should.Nil(err) should.Nil(err)
should.Equal("null", string(output)) should.Equal("null", string(output))
} }
func Test_decode_nil_num(t *testing.T) {
type TestData struct {
Field int `json:"field"`
}
should := require.New(t)
data1 := []byte(`{"field": 42}`)
data2 := []byte(`{"field": null}`)
// Checking stdlib behavior as well
obj2 := TestData{}
err := json.Unmarshal(data1, &obj2)
should.Equal(nil, err)
should.Equal(42, obj2.Field)
err = json.Unmarshal(data2, &obj2)
should.Equal(nil, err)
should.Equal(42, obj2.Field)
obj := TestData{}
err = Unmarshal(data1, &obj)
should.Equal(nil, err)
should.Equal(42, obj.Field)
err = Unmarshal(data2, &obj)
should.Equal(nil, err)
should.Equal(42, obj.Field)
}

View File

@ -95,267 +95,6 @@ func Test_write_object(t *testing.T) {
should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String()) should.Equal("{\n \"hello\": 1,\n \"world\": 2\n}", buf.String())
} }
func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj))
should.Equal("hello", obj.Field1)
}
func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
}
func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
}
func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
}
func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_six_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
}
func Test_decode_seven_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
}
func Test_decode_eight_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8":"1", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
should.Equal("1", obj.Field8)
}
func Test_decode_nine_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8" : "zzzzzzzzzzz", "Field7": "zz", "Field6" : "xx", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field9":"f"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("xx", obj.Field6)
should.Equal("zz", obj.Field7)
should.Equal("zzzzzzzzzzz", obj.Field8)
should.Equal("f", obj.Field9)
}
func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field10":"x", "Field9": "x", "Field8":"x", "Field7":"x", "Field6":"x", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("x", obj.Field7)
should.Equal("x", obj.Field8)
should.Equal("x", obj.Field9)
should.Equal("x", obj.Field10)
}
func Test_decode_more_than_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
Field11 int
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field11":1, "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal(1, obj.Field11)
}
func Test_decode_struct_field_with_tag(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1"`
Field2 string `json:"-"`
Field3 int `json:",string"`
}
obj := TestObject{Field2: "world"}
UnmarshalFromString(`{"field-1": "hello", "field2": "", "Field3": "100"}`, &obj)
should.Equal("hello", obj.Field1)
should.Equal("world", obj.Field2)
should.Equal(100, obj.Field3)
}
func Test_decode_struct_field_with_tag_string(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int `json:",string"`
}
obj := TestObject{Field1: 100}
should.Nil(UnmarshalFromString(`{"Field1": "100"}`, &obj))
should.Equal(100, obj.Field1)
}
func Test_write_val_zero_field_struct(t *testing.T) { func Test_write_val_zero_field_struct(t *testing.T) {
should := require.New(t) should := require.New(t)
type TestObject struct { type TestObject struct {

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)
}

View File

@ -19,6 +19,8 @@ func Test_read_string(t *testing.T) {
`"\"`, `"\"`,
`"\\\"`, `"\\\"`,
"\"\n\"", "\"\n\"",
`"\U0001f64f"`,
`"\uD83D\u00"`,
} }
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
// control characters are invalid // control characters are invalid
@ -39,6 +41,11 @@ func Test_read_string(t *testing.T) {
{`"a"`, "a"}, {`"a"`, "a"},
{`null`, ""}, {`null`, ""},
{`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"}, {`"Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"`, "Iñtërnâtiônàlizætiøn,💝🐹🌇⛔"},
{`"\uD83D"`, string([]byte{239, 191, 189})},
{`"\uD83D\\"`, string([]byte{239, 191, 189, '\\'})},
{`"\uD83D\ub000"`, string([]byte{239, 191, 189, 235, 128, 128})},
{`"\uD83D\ude04"`, "😄"},
{`"\uDEADBEEF"`, string([]byte{239, 191, 189, 66, 69, 69, 70})},
} }
for _, tc := range goodInputs { for _, tc := range goodInputs {
@ -111,7 +118,9 @@ func Test_read_exotic_string(t *testing.T) {
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) { t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t) should := require.New(t)
iter := ParseString(ConfigDefault, input) iter := ParseString(ConfigDefault, input)
should.Equal(output, iter.ReadString()) var v string
should.Nil(json.Unmarshal([]byte(input), &v))
should.Equal(v, iter.ReadString())
}) })
t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) { t.Run(fmt.Sprintf("%v:%v", input, output), func(t *testing.T) {
should := require.New(t) should := require.New(t)

View File

@ -0,0 +1,267 @@
package jsoniter
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_decode_one_field_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field1": "hello"}`, &obj))
should.Equal("hello", obj.Field1)
}
func Test_decode_two_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
}
func Test_decode_three_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
}
func Test_decode_four_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
}
func Test_decode_five_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
}
func Test_decode_six_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
}
func Test_decode_seven_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
}
func Test_decode_eight_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8":"1", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field6": "x", "Field7":"y"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("y", obj.Field7)
should.Equal("1", obj.Field8)
}
func Test_decode_nine_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field8" : "zzzzzzzzzzz", "Field7": "zz", "Field6" : "xx", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e", "Field9":"f"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("xx", obj.Field6)
should.Equal("zz", obj.Field7)
should.Equal("zzzzzzzzzzz", obj.Field8)
should.Equal("f", obj.Field9)
}
func Test_decode_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"Field10":"x", "Field9": "x", "Field8":"x", "Field7":"x", "Field6":"x", "Field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal("x", obj.Field6)
should.Equal("x", obj.Field7)
should.Equal("x", obj.Field8)
should.Equal("x", obj.Field9)
should.Equal("x", obj.Field10)
}
func Test_decode_more_than_ten_fields_struct(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
Field2 string
Field3 string
Field4 string
Field5 string
Field6 string
Field7 string
Field8 string
Field9 string
Field10 string
Field11 int
}
obj := TestObject{}
should.Nil(UnmarshalFromString(`{}`, &obj))
should.Equal("", obj.Field1)
should.Nil(UnmarshalFromString(`{"field11":1, "field1": "a", "Field2": "stream", "Field3": "c", "Field4": "d", "Field5": "e"}`, &obj))
should.Equal("a", obj.Field1)
should.Equal("stream", obj.Field2)
should.Equal("c", obj.Field3)
should.Equal("d", obj.Field4)
should.Equal("e", obj.Field5)
should.Equal(1, obj.Field11)
}
func Test_decode_struct_field_with_tag(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string `json:"field-1"`
Field2 string `json:"-"`
Field3 int `json:",string"`
}
obj := TestObject{Field2: "world"}
UnmarshalFromString(`{"field-1": "hello", "field2": "", "Field3": "100"}`, &obj)
should.Equal("hello", obj.Field1)
should.Equal("world", obj.Field2)
should.Equal(100, obj.Field3)
}
func Test_decode_struct_field_with_tag_string(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int `json:",string"`
}
obj := TestObject{Field1: 100}
should.Nil(UnmarshalFromString(`{"Field1": "100"}`, &obj))
should.Equal(100, obj.Field1)
}

View File

@ -15,80 +15,80 @@ func Test_wrap_and_valuetype_everything(t *testing.T) {
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int8(10)) any = Wrap(int8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// get interface is not int8 interface // get interface is not int8 interface
// i = int8(10) // i = int8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(int16(10)) any = Wrap(int16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
//i = int16(10) //i = int16(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(int32(10)) any = Wrap(int32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int32(10) i = int32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(int64(10)) any = Wrap(int64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = int64(10) i = int64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint(10)) any = Wrap(uint(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = uint(10) //i = uint(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(uint8(10)) any = Wrap(uint8(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
// i = uint8(10) // i = uint8(10)
// should.Equal(i, any.GetInterface()) // should.Equal(i, any.GetInterface())
any = Wrap(uint16(10)) any = Wrap(uint16(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(uint32(10)) any = Wrap(uint32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint32(10) i = uint32(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(uint64(10)) any = Wrap(uint64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = uint64(10) i = uint64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(float32(10)) any = Wrap(float32(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
// not equal // not equal
//i = float32(10) //i = float32(10)
//should.Equal(i, any.GetInterface()) //should.Equal(i, any.GetInterface())
any = Wrap(float64(10)) any = Wrap(float64(10))
should.Equal(any.ValueType(), Number) should.Equal(any.ValueType(), NumberValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = float64(10) i = float64(10)
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(true) any = Wrap(true)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = true i = true
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(false) any = Wrap(false)
should.Equal(any.ValueType(), Bool) should.Equal(any.ValueType(), BoolValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = false i = false
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(nil) any = Wrap(nil)
should.Equal(any.ValueType(), Nil) should.Equal(any.ValueType(), NilValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = nil i = nil
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
@ -99,13 +99,13 @@ func Test_wrap_and_valuetype_everything(t *testing.T) {
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
any = Wrap(struct{ age int }{age: 1}) any = Wrap(struct{ age int }{age: 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = struct{ age int }{age: 1} i = struct{ age int }{age: 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())
any = Wrap(map[string]interface{}{"abc": 1}) any = Wrap(map[string]interface{}{"abc": 1})
should.Equal(any.ValueType(), Object) should.Equal(any.ValueType(), ObjectValue)
should.Equal(any.LastError(), nil) should.Equal(any.LastError(), nil)
i = map[string]interface{}{"abc": 1} i = map[string]interface{}{"abc": 1}
should.Equal(i, any.GetInterface()) should.Equal(i, any.GetInterface())

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest int64

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest map[int64]string

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,3 @@
package test
type typeForTest []int64

View File

@ -8,9 +8,11 @@ type typeForTest struct {
Int8 int8 Int8 int8
Int16 int16 Int16 int16
Int32 int32 Int32 int32
Int64 int64
Uint8 uint8 Uint8 uint8
Uint16 uint16 Uint16 uint16
Uint32 uint32 Uint32 uint32
Uint64 uint64
Float32 float32 Float32 float32
Float64 float64 Float64 float64
String1 string String1 string

View File

@ -0,0 +1,152 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
fuzz "github.com/google/gofuzz"
jsoniter "github.com/json-iterator/go"
)
func Test_Roundtrip(t *testing.T) {
fz := fuzz.New().MaxDepth(10).NilChance(0.3)
for i := 0; i < 100; i++ {
var before typeForTest
fz.Fuzz(&before)
jbStd, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with stdlib: %v", err)
}
if len(strings.TrimSpace(string(jbStd))) == 0 {
t.Fatal("stdlib marshal produced empty result and no error")
}
jbIter, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal with jsoniter: %v", err)
}
if len(strings.TrimSpace(string(jbIter))) == 0 {
t.Fatal("jsoniter marshal produced empty result and no error")
}
if string(jbStd) != string(jbIter) {
t.Fatalf("marshal expected:\n %s\ngot:\n %s\nobj:\n %s",
indent(jbStd, " "), indent(jbIter, " "), dump(before))
}
var afterStd typeForTest
err = json.Unmarshal(jbIter, &afterStd)
if err != nil {
t.Fatalf("failed to unmarshal with stdlib: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
var afterIter typeForTest
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(jbIter, &afterIter)
if err != nil {
t.Fatalf("failed to unmarshal with jsoniter: %v\nvia:\n %s",
err, indent(jbIter, " "))
}
if fingerprint(afterStd) != fingerprint(afterIter) {
t.Fatalf("unmarshal expected:\n %s\ngot:\n %s\nvia:\n %s",
dump(afterStd), dump(afterIter), indent(jbIter, " "))
}
}
}
const indentStr = "> "
func fingerprint(obj interface{}) string {
c := spew.ConfigState{
SortKeys: true,
SpewKeys: true,
}
return c.Sprintf("%v", obj)
}
func dump(obj interface{}) string {
cfg := spew.ConfigState{
Indent: indentStr,
}
return cfg.Sdump(obj)
}
func indent(src []byte, prefix string) string {
var buf bytes.Buffer
err := json.Indent(&buf, src, prefix, indentStr)
if err != nil {
return fmt.Sprintf("!!! %v", err)
}
return buf.String()
}
func benchmarkMarshal(t *testing.B, name string, fn func(interface{}) ([]byte, error)) {
t.ReportAllocs()
t.ResetTimer()
var obj typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&obj)
for i := 0; i < t.N; i++ {
jb, err := fn(obj)
if err != nil {
t.Fatalf("%s failed to marshal:\n input: %s\n error: %v", name, dump(obj), err)
}
_ = jb
}
}
func benchmarkUnmarshal(t *testing.B, name string, fn func(data []byte, v interface{}) error) {
t.ReportAllocs()
t.ResetTimer()
var before typeForTest
fz := fuzz.NewWithSeed(0).MaxDepth(10).NilChance(0.3)
fz.Fuzz(&before)
jb, err := json.Marshal(before)
if err != nil {
t.Fatalf("failed to marshal: %v", err)
}
for i := 0; i < t.N; i++ {
var after typeForTest
err = fn(jb, &after)
if err != nil {
t.Fatalf("%s failed to unmarshal:\n input: %q\n error: %v", name, string(jb), err)
}
}
}
func BenchmarkStandardMarshal(t *testing.B) {
benchmarkMarshal(t, "stdlib", json.Marshal)
}
func BenchmarkStandardUnmarshal(t *testing.B) {
benchmarkUnmarshal(t, "stdlib", json.Unmarshal)
}
func BenchmarkJSONIterMarshalFastest(t *testing.B) {
benchmarkMarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Marshal)
}
func BenchmarkJSONIterUnmarshalFastest(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-fastest", jsoniter.ConfigFastest.Unmarshal)
}
func BenchmarkJSONIterMarshalDefault(t *testing.B) {
benchmarkMarshal(t, "jsoniter-default", jsoniter.Marshal)
}
func BenchmarkJSONIterUnmarshalDefault(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-default", jsoniter.Unmarshal)
}
func BenchmarkJSONIterMarshalCompatible(t *testing.B) {
benchmarkMarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Marshal)
}
func BenchmarkJSONIterUnmarshalCompatible(t *testing.B) {
benchmarkUnmarshal(t, "jsoniter-compat", jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal)
}

View File

@ -0,0 +1,5 @@
package test
type typeForTest struct {
F int64
}

View File

@ -13,4 +13,5 @@ var inputs = []string{
"1E1", // valid, e or E "1E1", // valid, e or E
"1ee1", // invalid "1ee1", // invalid
"100a", // invalid "100a", // invalid
"10.", // invalid
} }