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

45 Commits
1.0.0 ... 1.0.3

Author SHA1 Message Date
6240e1e798 #185 add jsoniter.Valid 2017-10-10 08:57:02 +08:00
0149a5cf4a fix #183 error message not only show expectation, but also the actual value 2017-10-09 08:24:51 +08:00
5068c8baaf #183 limit error message size 2017-10-09 08:16:52 +08:00
16f78601b5 fix #184, support null as number 2017-10-07 09:29:32 +08:00
8f50a91be2 fix #181, support string as json.Number and jsoniter.Number 2017-10-06 18:08:14 +08:00
73c7bc881e fix #180, add missing methods to jsoniter.Number 2017-10-06 17:56:36 +08:00
4de15a3a87 Merge pull request #182 from MOZGIII/patch-1
Used writeTwoBytes in Stream.WriteEmptyArray
2017-10-04 02:25:32 -05:00
14b28b2226 Used writeTwoBytes in Stream.WriteEmptyArray 2017-10-03 22:14:36 +03:00
abe3c4016b fix #179 2017-09-26 15:35:55 +08:00
dbb1ef3f63 #177 flush buffer should check available again 2017-09-21 21:04:45 +08:00
46b20bbbec #178 SkipAndReturnBytes should return copy of memory 2017-09-21 20:18:45 +08:00
fdfe0b9a69 Merge branch 'olegshaldybin-skip-unexported-fields' 2017-09-19 10:06:59 +08:00
faa3dcf46a do not report error when field is unexported 2017-09-19 10:06:34 +08:00
1f58120d43 Always skip unexported fields when encoding
Skip creating encoders for unexported fields. They are not participating
in JSON marshaling anyway. This allows using unexported fields of
non-marshalable types in structs.

As a side-effect of this change it's no longer possible to marshal
unexported JSON fields by adding a custom type extenstion. It seems this
is desired behavior since it matches standard library and jsoniter
already disallows `json:"-"` fields from participating in custom
extensions.

Fixes #174.
2017-09-18 11:02:15 -07:00
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
27 changed files with 948 additions and 73 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

@ -44,7 +44,9 @@ with
```go ```go
import "github.com/json-iterator/go" import "github.com/json-iterator/go"
jsoniter.Marshal(&data)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Marshal(&data)
``` ```
Replace Replace
@ -58,7 +60,9 @@ with
```go ```go
import "github.com/json-iterator/go" import "github.com/json-iterator/go"
jsoniter.Unmarshal(input, &data)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(input, &data)
``` ```
[More documentation](http://jsoniter.com/migrate-from-go-std.html) [More documentation](http://jsoniter.com/migrate-from-go-std.html)
@ -76,5 +80,7 @@ Contributors
* [thockin](https://github.com/thockin) * [thockin](https://github.com/thockin)
* [mattn](https://github.com/mattn) * [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)
@ -199,6 +201,12 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
str = string(number) str = string(number)
case jsoniter.StringValue: case jsoniter.StringValue:
str = iter.ReadString() str = iter.ReadString()
case jsoniter.BoolValue:
if iter.ReadBool() {
str = "1"
} else {
str = "0"
}
default: default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string") iter.ReportError("fuzzyIntegerDecoder", "not number or string")
} }
@ -206,7 +214,7 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1 isFloat := strings.IndexByte(str, '.') != -1
decoder.fun(isFloat, ptr, newIter) decoder.fun(isFloat, ptr, newIter)
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
} }
@ -225,9 +233,16 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float32)(ptr)) = newIter.ReadFloat32() *((*float32)(ptr)) = newIter.ReadFloat32()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float32
if iter.ReadBool() {
*((*float32)(ptr)) = 1
} else {
*((*float32)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }
@ -247,9 +262,16 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
newIter := iter.Pool().BorrowIterator([]byte(str)) newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter) defer iter.Pool().ReturnIterator(newIter)
*((*float64)(ptr)) = newIter.ReadFloat64() *((*float64)(ptr)) = newIter.ReadFloat64()
if newIter.Error != nil { if newIter.Error != nil && newIter.Error != io.EOF {
iter.Error = newIter.Error iter.Error = newIter.Error
} }
case jsoniter.BoolValue:
// support bool to float64
if iter.ReadBool() {
*((*float64)(ptr)) = 1
} else {
*((*float64)(ptr)) = 0
}
default: default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string") iter.ReportError("fuzzyFloat32Decoder", "not number or string")
} }

View File

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

View File

@ -125,3 +125,8 @@ func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
config.EscapeHTML = escapeHTML config.EscapeHTML = escapeHTML
adapter.stream.cfg = config.Froze().(*frozenConfig) adapter.stream.cfg = config.Froze().(*frozenConfig)
} }
// Valid reports whether data is a valid JSON encoding.
func Valid(data []byte) bool {
return ConfigDefault.Valid(data)
}

View File

@ -18,6 +18,7 @@ type Config struct {
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
TagKey string TagKey string
ValidateJsonRawMessage bool
} }
type frozenConfig struct { type frozenConfig struct {
@ -44,6 +45,7 @@ type API interface {
Get(data []byte, path ...interface{}) Any Get(data []byte, path ...interface{}) Any
NewEncoder(writer io.Writer) *Encoder NewEncoder(writer io.Writer) *Encoder
NewDecoder(reader io.Reader) *Decoder NewDecoder(reader io.Reader) *Decoder
Valid(data []byte) bool
} }
// ConfigDefault the default API // ConfigDefault the default API
@ -53,8 +55,9 @@ var ConfigDefault = Config{
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior // ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
var ConfigCompatibleWithStandardLibrary = Config{ var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true, EscapeHTML: true,
SortMapKeys: true, SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze() }.Froze()
// ConfigFastest marshals float with only 6 digits precision // ConfigFastest marshals float with only 6 digits precision
@ -83,10 +86,31 @@ func (cfg Config) Froze() API {
if cfg.UseNumber { if cfg.UseNumber {
frozenConfig.useNumber() frozenConfig.useNumber()
} }
if cfg.ValidateJsonRawMessage {
frozenConfig.validateJsonRawMessage()
}
frozenConfig.configBeforeFrozen = cfg frozenConfig.configBeforeFrozen = cfg
return frozenConfig return frozenConfig
} }
func (cfg *frozenConfig) validateJsonRawMessage() {
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
rawMessage := *(*json.RawMessage)(ptr)
iter := cfg.BorrowIterator([]byte(rawMessage))
iter.Read()
if iter.Error != nil {
stream.WriteRaw("null")
} else {
cfg.ReturnIterator(iter)
stream.WriteRaw(string(rawMessage))
}
}, func(ptr unsafe.Pointer) bool {
return false
}}
cfg.addEncoderToCache(reflect.TypeOf((*json.RawMessage)(nil)).Elem(), encoder)
cfg.addEncoderToCache(reflect.TypeOf((*RawMessage)(nil)).Elem(), encoder)
}
func (cfg *frozenConfig) useNumber() { func (cfg *frozenConfig) useNumber() {
cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { cfg.addDecoderToCache(reflect.TypeOf((*interface{})(nil)).Elem(), &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == NumberValue { if iter.WhatIsNext() == NumberValue {
@ -310,3 +334,10 @@ func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
iter := Parse(cfg, reader, 512) iter := Parse(cfg, reader, 512)
return &Decoder{iter} return &Decoder{iter}
} }
func (cfg *frozenConfig) Valid(data []byte) bool {
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
iter.Skip()
return iter.Error == nil
}

View File

@ -200,8 +200,22 @@ func (iter *Iterator) ReportError(operation string, msg string) {
if peekStart < 0 { if peekStart < 0 {
peekStart = 0 peekStart = 0
} }
iter.Error = fmt.Errorf("%s: %s, parsing %v ...%s... at %s", operation, msg, iter.head, peekEnd := iter.head + 10
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) if peekEnd > iter.tail {
peekEnd = iter.tail
}
parsing := string(iter.buf[peekStart:peekEnd])
contextStart := iter.head - 50
if contextStart < 0 {
contextStart = 0
}
contextEnd := iter.head + 50
if contextEnd > iter.tail {
contextEnd = iter.tail
}
context := string(iter.buf[contextStart:contextEnd])
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...",
operation, msg, iter.head-peekStart, parsing, context)
} }
// CurrentBuffer gets current buffer as string for debugging purpose // CurrentBuffer gets current buffer as string for debugging purpose
@ -210,7 +224,7 @@ func (iter *Iterator) CurrentBuffer() string {
if peekStart < 0 { if peekStart < 0 {
peekStart = 0 peekStart = 0
} }
return fmt.Sprintf("parsing %v ...|%s|... at %s", iter.head, return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head,
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
} }

View File

@ -19,7 +19,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
case ',': case ',':
return true return true
default: default:
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found: "+string([]byte{c})) iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c}))
return return
} }
} }
@ -42,7 +42,7 @@ func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
c = iter.nextToken() c = iter.nextToken()
} }
if c != ']' { if c != ']' {
iter.ReportError("ReadArrayCB", "expect ] in the end") iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
return false return false
} }
return true return true
@ -53,6 +53,6 @@ func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null return true // null
} }
iter.ReportError("ReadArrayCB", "expect [ or n, but found: "+string([]byte{c})) iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c}))
return false return false
} }

View File

@ -24,7 +24,7 @@ func (iter *Iterator) ReadObject() (ret string) {
if c == '}' { if c == '}' {
return "" // end of object return "" // end of object
} }
iter.ReportError("ReadObject", `expect " after {`) iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
return return
case ',': case ',':
return string(iter.readObjectFieldAsBytes()) return string(iter.readObjectFieldAsBytes())
@ -105,14 +105,14 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
if c == '}' { if c == '}' {
return true return true
} }
iter.ReportError("ReadObjectCB", `expect " after }`) iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c}))
return false return false
} }
if c == 'n' { if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null return true // null
} }
iter.ReportError("ReadObjectCB", `expect { or n`) iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
return false return false
} }
@ -125,7 +125,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
iter.unreadByte() iter.unreadByte()
field := iter.ReadString() field := iter.ReadString()
if iter.nextToken() != ':' { if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field") iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
return false return false
} }
if !callback(iter, field) { if !callback(iter, field) {
@ -135,7 +135,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
for c == ',' { for c == ',' {
field = iter.ReadString() field = iter.ReadString()
if iter.nextToken() != ':' { if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field") iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
return false return false
} }
if !callback(iter, field) { if !callback(iter, field) {
@ -152,14 +152,14 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
if c == '}' { if c == '}' {
return true return true
} }
iter.ReportError("ReadMapCB", `expect " after }`) iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c}))
return false return false
} }
if c == 'n' { if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return true // null return true // null
} }
iter.ReportError("ReadMapCB", `expect { or n`) iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
return false return false
} }
@ -176,7 +176,7 @@ func (iter *Iterator) readObjectStart() bool {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return false return false
} }
iter.ReportError("readObjectStart", "expect { or n") iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
return false return false
} }
@ -192,7 +192,7 @@ func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
} }
} }
if iter.buf[iter.head] != ':' { if iter.buf[iter.head] != ':' {
iter.ReportError("readObjectFieldAsBytes", "expect : after object field") iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
return return
} }
iter.head++ iter.head++

View File

@ -25,7 +25,7 @@ func (iter *Iterator) ReadBool() (ret bool) {
iter.skipFourBytes('a', 'l', 's', 'e') iter.skipFourBytes('a', 'l', 's', 'e')
return false return false
} }
iter.ReportError("ReadBool", "expect t or f") iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c}))
return return
} }
@ -59,7 +59,9 @@ func (iter *Iterator) stopCapture() []byte {
iter.captureStartedAt = -1 iter.captureStartedAt = -1
iter.captured = nil iter.captured = nil
if len(captured) == 0 { if len(captured) == 0 {
return remaining copied := make([]byte, len(remaining))
copy(copied, remaining)
return copied
} }
captured = append(captured, remaining...) captured = append(captured, remaining...)
return captured return captured

View File

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

View File

@ -28,7 +28,7 @@ func (iter *Iterator) ReadString() (ret string) {
iter.skipThreeBytes('u', 'l', 'l') iter.skipThreeBytes('u', 'l', 'l')
return "" return ""
} }
iter.ReportError("ReadString", `expects " or n`) iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
return return
} }
@ -47,7 +47,7 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
str = append(str, c) str = append(str, c)
} }
} }
iter.ReportError("ReadString", "unexpected end of input") iter.ReportError("readStringSlowPath", "unexpected end of input")
return return
} }
@ -104,7 +104,7 @@ func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
case 't': case 't':
str = append(str, '\t') str = append(str, '\t')
default: default:
iter.ReportError("ReadString", iter.ReportError("readEscapedChar",
`invalid escape char after \`) `invalid escape char after \`)
return nil return nil
} }
@ -139,7 +139,7 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
} }
return copied return copied
} }
iter.ReportError("ReadString", `expects " or n`) iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
return return
} }
@ -156,7 +156,7 @@ func (iter *Iterator) readU4() (ret rune) {
} else if c >= 'A' && c <= 'F' { } else if c >= 'A' && c <= 'F' {
ret = ret*16 + rune(c-'A'+10) ret = ret*16 + rune(c-'A'+10)
} else { } else {
iter.ReportError("readU4", "expects 0~9 or a~f") iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
return return
} }
} }

View File

@ -1,9 +1,25 @@
package jsoniter package jsoniter
import "encoding/json" import (
"encoding/json"
"strconv"
)
type Number string type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
func CastJsonNumber(val interface{}) (string, bool) { func CastJsonNumber(val interface{}) (string, bool) {
switch typedVal := val.(type) { switch typedVal := val.(type) {
case json.Number: case json.Number:

View File

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

View File

@ -269,7 +269,7 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
if decoder == nil { if decoder == nil {
var err error var err error
decoder, err = decoderOfType(cfg, field.Type) decoder, err = decoderOfType(cfg, field.Type)
if err != nil { if len(fieldNames) > 0 && err != nil {
return nil, err return nil, err
} }
} }
@ -277,11 +277,11 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
if encoder == nil { if encoder == nil {
var err error var err error
encoder, err = encoderOfType(cfg, field.Type) encoder, err = encoderOfType(cfg, field.Type)
if err != nil { if len(fieldNames) > 0 && err != nil {
return nil, err return nil, err
} }
// map is stored as pointer in the struct // map is stored as pointer in the struct
if field.Type.Kind() == reflect.Map { if encoder != nil && field.Type.Kind() == reflect.Map {
encoder = &optionalEncoder{encoder} encoder = &optionalEncoder{encoder}
} }
} }

View File

@ -4,6 +4,7 @@ import (
"encoding" "encoding"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"reflect"
"unsafe" "unsafe"
) )
@ -31,7 +32,9 @@ type intCodec struct {
} }
func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *intCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int)(ptr)) = iter.ReadInt() if !iter.ReadNil() {
*((*int)(ptr)) = iter.ReadInt()
}
} }
func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *intCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -50,7 +53,9 @@ type uintptrCodec struct {
} }
func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintptrCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64()) if !iter.ReadNil() {
*((*uintptr)(ptr)) = uintptr(iter.ReadUint64())
}
} }
func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintptrCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -69,7 +74,9 @@ type int8Codec struct {
} }
func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int8)(ptr)) = iter.ReadInt8() if !iter.ReadNil() {
*((*int8)(ptr)) = iter.ReadInt8()
}
} }
func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -88,7 +95,9 @@ type int16Codec struct {
} }
func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int16)(ptr)) = iter.ReadInt16() if !iter.ReadNil() {
*((*int16)(ptr)) = iter.ReadInt16()
}
} }
func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -107,7 +116,9 @@ type int32Codec struct {
} }
func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int32)(ptr)) = iter.ReadInt32() if !iter.ReadNil() {
*((*int32)(ptr)) = iter.ReadInt32()
}
} }
func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -126,7 +137,9 @@ type int64Codec struct {
} }
func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*int64)(ptr)) = iter.ReadInt64() if !iter.ReadNil() {
*((*int64)(ptr)) = iter.ReadInt64()
}
} }
func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -145,7 +158,10 @@ type uintCodec struct {
} }
func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uintCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint)(ptr)) = iter.ReadUint() if !iter.ReadNil() {
*((*uint)(ptr)) = iter.ReadUint()
return
}
} }
func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uintCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -164,7 +180,9 @@ type uint8Codec struct {
} }
func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint8)(ptr)) = iter.ReadUint8() if !iter.ReadNil() {
*((*uint8)(ptr)) = iter.ReadUint8()
}
} }
func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -183,7 +201,9 @@ type uint16Codec struct {
} }
func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint16)(ptr)) = iter.ReadUint16() if !iter.ReadNil() {
*((*uint16)(ptr)) = iter.ReadUint16()
}
} }
func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -202,7 +222,9 @@ type uint32Codec struct {
} }
func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint32)(ptr)) = iter.ReadUint32() if !iter.ReadNil() {
*((*uint32)(ptr)) = iter.ReadUint32()
}
} }
func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -221,7 +243,9 @@ type uint64Codec struct {
} }
func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*uint64)(ptr)) = iter.ReadUint64() if !iter.ReadNil() {
*((*uint64)(ptr)) = iter.ReadUint64()
}
} }
func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -240,7 +264,9 @@ type float32Codec struct {
} }
func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float32)(ptr)) = iter.ReadFloat32() if !iter.ReadNil() {
*((*float32)(ptr)) = iter.ReadFloat32()
}
} }
func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -259,7 +285,9 @@ type float64Codec struct {
} }
func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*float64)(ptr)) = iter.ReadFloat64() if !iter.ReadNil() {
*((*float64)(ptr)) = iter.ReadFloat64()
}
} }
func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -278,7 +306,9 @@ type boolCodec struct {
} }
func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*bool)(ptr)) = iter.ReadBool() if !iter.ReadNil() {
*((*bool)(ptr)) = iter.ReadBool()
}
} }
func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -297,7 +327,42 @@ type emptyInterfaceCodec struct {
} }
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*interface{})(ptr)) = iter.Read() existing := *((*interface{})(ptr))
// Checking for both typed and untyped nil pointers.
if existing != nil &&
reflect.TypeOf(existing).Kind() == reflect.Ptr &&
!reflect.ValueOf(existing).IsNil() {
var ptrToExisting interface{}
for {
elem := reflect.ValueOf(existing).Elem()
if elem.Kind() != reflect.Ptr || elem.IsNil() {
break
}
ptrToExisting = existing
existing = elem.Interface()
}
if iter.ReadNil() {
if ptrToExisting != nil {
nilPtr := reflect.Zero(reflect.TypeOf(ptrToExisting).Elem())
reflect.ValueOf(ptrToExisting).Elem().Set(nilPtr)
} else {
*((*interface{})(ptr)) = nil
}
} else {
iter.ReadVal(existing)
}
return
}
if iter.ReadNil() {
*((*interface{})(ptr)) = nil
} else {
*((*interface{})(ptr)) = iter.Read()
}
} }
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -309,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 {
@ -326,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{}
e := (*emptyInterface)(unsafe.Pointer(&i)) if nonEmptyInterface.itab != nil {
e.typ = nonEmptyInterface.itab.typ e := (*emptyInterface)(unsafe.Pointer(&i))
e.word = nonEmptyInterface.word e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
}
stream.WriteVal(i) stream.WriteVal(i)
} }
@ -370,7 +441,15 @@ type jsonNumberCodec struct {
} }
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString())) switch iter.WhatIsNext() {
case StringValue:
*((*json.Number)(ptr)) = json.Number(iter.ReadString())
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*json.Number)(ptr)) = ""
default:
*((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString()))
}
} }
func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -389,7 +468,15 @@ type jsoniterNumberCodec struct {
} }
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString())) switch iter.WhatIsNext() {
case StringValue:
*((*Number)(ptr)) = Number(iter.ReadString())
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*Number)(ptr)) = ""
default:
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
}
} }
func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
@ -521,7 +608,7 @@ type stringModeNumberDecoder struct {
func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
c := iter.nextToken() c := iter.nextToken()
if c != '"' { if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`) iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c}))
return return
} }
decoder.elemDecoder.Decode(ptr, iter) decoder.elemDecoder.Decode(ptr, iter)
@ -530,7 +617,7 @@ func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
} }
c = iter.readByte() c = iter.readByte()
if c != '"' { if c != '"' {
iter.ReportError("stringModeNumberDecoder", `expect "`) iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c}))
return return
} }
} }
@ -595,7 +682,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

@ -191,6 +191,9 @@ func (stream *Stream) ensure(minimal int) {
func (stream *Stream) growAtLeast(minimal int) { func (stream *Stream) growAtLeast(minimal int) {
if stream.out != nil { if stream.out != nil {
stream.Flush() stream.Flush()
if stream.Available() >= minimal {
return
}
} }
toGrow := len(stream.buf) toGrow := len(stream.buf)
if toGrow < minimal { if toGrow < minimal {
@ -280,8 +283,7 @@ func (stream *Stream) WriteArrayStart() {
// WriteEmptyArray write [] // WriteEmptyArray write []
func (stream *Stream) WriteEmptyArray() { func (stream *Stream) WriteEmptyArray() {
stream.writeByte('[') stream.writeTwoBytes('[', ']')
stream.writeByte(']')
} }
// WriteArrayEnd write ] with possible indention // WriteArrayEnd write ] with possible indention

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

@ -2,11 +2,12 @@ package jsoniter
import ( import (
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require"
"strconv" "strconv"
"testing" "testing"
"time" "time"
"unsafe" "unsafe"
"github.com/stretchr/testify/require"
) )
func Test_customize_type_decoder(t *testing.T) { func Test_customize_type_decoder(t *testing.T) {
@ -82,7 +83,7 @@ func Test_customize_field_decoder(t *testing.T) {
} }
type TestObject1 struct { type TestObject1 struct {
field1 string Field1 string
} }
type testExtension struct { type testExtension struct {
@ -93,7 +94,7 @@ func (extension *testExtension) UpdateStructDescriptor(structDescriptor *StructD
if structDescriptor.Type.String() != "jsoniter.TestObject1" { if structDescriptor.Type.String() != "jsoniter.TestObject1" {
return return
} }
binding := structDescriptor.GetField("field1") binding := structDescriptor.GetField("Field1")
binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *Stream) { binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *Stream) {
str := *((*string)(ptr)) str := *((*string)(ptr))
val, _ := strconv.Atoi(str) val, _ := strconv.Atoi(str)
@ -112,7 +113,7 @@ func Test_customize_field_by_extension(t *testing.T) {
obj := TestObject1{} obj := TestObject1{}
err := UnmarshalFromString(`{"field-1": 100}`, &obj) err := UnmarshalFromString(`{"field-1": 100}`, &obj)
should.Nil(err) should.Nil(err)
should.Equal("100", obj.field1) should.Equal("100", obj.Field1)
str, err := MarshalToString(obj) str, err := MarshalToString(obj)
should.Nil(err) should.Nil(err)
should.Equal(`{"field-1":100}`, str) should.Equal(`{"field-1":100}`, str)
@ -314,3 +315,27 @@ func Test_recursive_empty_interface_customization(t *testing.T) {
Unmarshal([]byte("[100]"), &obj) Unmarshal([]byte("[100]"), &obj)
should.Equal([]interface{}{int64(100)}, obj) should.Equal([]interface{}{int64(100)}, obj)
} }
type GeoLocation struct {
Id string `json:"id,omitempty" db:"id"`
}
func (p *GeoLocation) MarshalJSON() ([]byte, error) {
return []byte(`{}`), nil
}
func (p *GeoLocation) UnmarshalJSON(input []byte) error {
p.Id = "hello"
return nil
}
func Test_marshal_and_unmarshal_on_non_pointer(t *testing.T) {
should := require.New(t)
locations := []GeoLocation{{"000"}}
bytes, err := Marshal(locations)
should.Nil(err)
should.Equal("[{}]", string(bytes))
err = Unmarshal([]byte("[1]"), &locations)
should.Nil(err)
should.Equal("hello", locations[0].Id)
}

View File

@ -0,0 +1,50 @@
package jsoniter
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
type MyEnum int64
const (
MyEnumA MyEnum = iota
MyEnumB
)
func (m *MyEnum) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"foo-%d"`, int(*m))), nil
}
func (m *MyEnum) UnmarshalJSON(jb []byte) error {
switch string(jb) {
case `"foo-1"`:
*m = MyEnumB
default:
*m = MyEnumA
}
return nil
}
func Test_custom_marshaler_on_enum(t *testing.T) {
type Wrapper struct {
Payload interface{}
}
type Wrapper2 struct {
Payload MyEnum
}
should := require.New(t)
w := Wrapper{Payload: MyEnumB}
jb, err := Marshal(w)
should.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) {
@ -468,6 +506,30 @@ func Test_jsoniter_number(t *testing.T) {
should.Equal("1", str) should.Equal("1", str)
} }
func Test_non_numeric_as_number(t *testing.T) {
should := require.New(t)
var v1 json.Number
err := Unmarshal([]byte(`"500"`), &v1)
should.Nil(err)
should.Equal("500", string(v1))
var v2 Number
err = Unmarshal([]byte(`"500"`), &v2)
should.Nil(err)
should.Equal("500", string(v2))
}
func Test_null_as_number(t *testing.T) {
should := require.New(t)
var v1 json.Number
err := json.Unmarshal([]byte(`null`), &v1)
should.Nil(err)
should.Equal("", string(v1))
var v2 Number
err = Unmarshal([]byte(`null`), &v2)
should.Nil(err)
should.Equal("", string(v2))
}
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,11 +1,11 @@
package jsoniter package jsoniter
import ( import (
"bytes"
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io" "io"
"testing" "testing"
"bytes"
) )
func Test_missing_object_end(t *testing.T) { func Test_missing_object_end(t *testing.T) {
@ -130,3 +130,9 @@ func Test_invalid_number(t *testing.T) {
should.Nil(err) should.Nil(err)
should.Equal(string(result2), string(result)) should.Equal(string(result2), string(result))
} }
func Test_valid(t *testing.T) {
should := require.New(t)
should.True(Valid([]byte(`{}`)))
should.False(Valid([]byte(`{`)))
}

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

@ -3,6 +3,7 @@ package jsoniter
import ( import (
"encoding/json" "encoding/json"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"strings"
"testing" "testing"
) )
@ -72,3 +73,42 @@ 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)
}
func Test_raw_message_memory_not_copied_issue(t *testing.T) {
jsonStream := `{"name":"xxxxx","bundle_id":"com.zonst.majiang","app_platform":"ios","app_category":"100103", "budget_day":1000,"bidding_min":1,"bidding_max":2,"bidding_type":"CPM", "freq":{"open":true,"type":"day","num":100},"speed":1, "targeting":{"vendor":{"open":true,"list":["zonst"]}, "geo_code":{"open":true,"list":["156110100"]},"app_category":{"open":true,"list":["100101"]}, "day_parting":{"open":true,"list":["100409","100410"]},"device_type":{"open":true,"list":["ipad"]}, "os_version":{"open":true,"list":[10]},"carrier":{"open":true,"list":["mobile"]}, "network":{"open":true,"list":["4G"]}},"url":{"tracking_imp_url":"http://www.baidu.com", "tracking_clk_url":"http://www.baidu.com","jump_url":"http://www.baidu.com","deep_link_url":"http://www.baidu.com"}}`
type IteratorObject struct {
Name *string `json:"name"`
BundleId *string `json:"bundle_id"`
AppCategory *string `json:"app_category"`
AppPlatform *string `json:"app_platform"`
BudgetDay *float32 `json:"budget_day"`
BiddingMax *float32 `json:"bidding_max"`
BiddingMin *float32 `json:"bidding_min"`
BiddingType *string `json:"bidding_type"`
Freq *RawMessage `json:"freq"`
Targeting *RawMessage `json:"targeting"`
Url *RawMessage `json:"url"`
Speed *int `json:"speed" db:"speed"`
}
obj := &IteratorObject{}
decoder := NewDecoder(strings.NewReader(jsonStream))
err := decoder.Decode(obj)
should := require.New(t)
should.Nil(err)
should.Equal(`{"open":true,"type":"day","num":100}`, string(*obj.Freq))
}

View File

@ -51,3 +51,19 @@ func Test_writeString_should_grow_buffer(t *testing.T) {
should.Nil(stream.Error) should.Nil(stream.Error)
should.Equal(`"123"`, string(stream.Buffer())) should.Equal(`"123"`, string(stream.Buffer()))
} }
type NopWriter struct {
bufferSize int
}
func (w *NopWriter) Write(p []byte) (n int, err error) {
w.bufferSize = cap(p)
return len(p), nil
}
func Test_flush_buffer_should_stop_grow_buffer(t *testing.T) {
writer := new(NopWriter)
NewEncoder(writer).Encode(make([]int, 10000000))
should := require.New(t)
should.Equal(512, writer.bufferSize)
}

View File

@ -0,0 +1,52 @@
package jsoniter
import (
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func Test_encode_unexported_field(t *testing.T) {
type TestData struct {
a int
b <-chan int
C int
d *time.Timer
}
should := require.New(t)
testChan := make(<-chan int, 10)
testTimer := time.NewTimer(10 * time.Second)
obj := &TestData{
a: 42,
b: testChan,
C: 21,
d: testTimer,
}
jb, err := json.Marshal(obj)
should.NoError(err)
should.Equal([]byte(`{"C":21}`), jb)
err = json.Unmarshal([]byte(`{"a": 444, "b":"bad", "C":55, "d":{"not": "a timer"}}`), obj)
should.NoError(err)
should.Equal(42, obj.a)
should.Equal(testChan, obj.b)
should.Equal(55, obj.C)
should.Equal(testTimer, obj.d)
jb, err = Marshal(obj)
should.NoError(err)
should.Equal(jb, []byte(`{"C":55}`))
err = Unmarshal([]byte(`{"a": 444, "b":"bad", "C":256, "d":{"not":"a timer"}}`), obj)
should.NoError(err)
should.Equal(42, obj.a)
should.Equal(testChan, obj.b)
should.Equal(256, obj.C)
should.Equal(testTimer, obj.d)
}