You've already forked json-iterator
mirror of
https://github.com/json-iterator/go.git
synced 2025-06-15 22:50:24 +02:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
1624edc445 | |||
5d789e5e02 | |||
0260c89b54 | |||
10a568c511 | |||
ab8a2e0c74 | |||
2fbdfbb595 | |||
720ab8dc7f | |||
f2b4162afb | |||
3830516ed0 | |||
7cceb6c2e3 | |||
b92cf78708 | |||
8744d7c5c7 | |||
37cc313d18 | |||
2ddf6d7582 | |||
6a6742f0a2 | |||
6c702ce12a | |||
f88871b601 | |||
f246f80f14 | |||
51dd70305b | |||
a949c42748 | |||
f89479f5c0 | |||
b858ec296c | |||
885a41a0a6 | |||
9e9a97040e | |||
fb4d53e4cc | |||
b53656d459 | |||
8f27a81d90 | |||
4930b053b8 | |||
06e0f9391e |
6
Gopkg.lock
generated
6
Gopkg.lock
generated
@ -10,12 +10,12 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/modern-go/reflect2"
|
name = "github.com/modern-go/reflect2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "ac7003b5a981716353a43055ab7d4c5357403cb30a60de2dbdeb446c1544beaa"
|
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -23,4 +23,4 @@ ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com
|
|||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/modern-go/reflect2"
|
name = "github.com/modern-go/reflect2"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
|
@ -31,6 +31,9 @@ Raw Result (easyjson requires static code generation)
|
|||||||
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op |
|
||||||
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op |
|
||||||
|
|
||||||
|
Always benchmark with your own workload.
|
||||||
|
The result depends heavily on the data input.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
100% compatibility with standard lib
|
100% compatibility with standard lib
|
||||||
|
19
adapter.go
19
adapter.go
@ -77,7 +77,16 @@ func (adapter *Decoder) Decode(obj interface{}) error {
|
|||||||
|
|
||||||
// More is there more?
|
// More is there more?
|
||||||
func (adapter *Decoder) More() bool {
|
func (adapter *Decoder) More() bool {
|
||||||
return adapter.iter.head != adapter.iter.tail
|
iter := adapter.iter
|
||||||
|
if iter.Error != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iter.unreadByte()
|
||||||
|
return c != ']' && c != '}'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffered remaining buffer
|
// Buffered remaining buffer
|
||||||
@ -91,7 +100,7 @@ func (adapter *Decoder) Buffered() io.Reader {
|
|||||||
func (adapter *Decoder) UseNumber() {
|
func (adapter *Decoder) UseNumber() {
|
||||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||||
cfg.UseNumber = true
|
cfg.UseNumber = true
|
||||||
adapter.iter.cfg = cfg.frozeWithCacheReuse()
|
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
||||||
@ -100,7 +109,7 @@ func (adapter *Decoder) UseNumber() {
|
|||||||
func (adapter *Decoder) DisallowUnknownFields() {
|
func (adapter *Decoder) DisallowUnknownFields() {
|
||||||
cfg := adapter.iter.cfg.configBeforeFrozen
|
cfg := adapter.iter.cfg.configBeforeFrozen
|
||||||
cfg.DisallowUnknownFields = true
|
cfg.DisallowUnknownFields = true
|
||||||
adapter.iter.cfg = cfg.frozeWithCacheReuse()
|
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEncoder same as json.NewEncoder
|
// NewEncoder same as json.NewEncoder
|
||||||
@ -125,14 +134,14 @@ func (adapter *Encoder) Encode(val interface{}) error {
|
|||||||
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
func (adapter *Encoder) SetIndent(prefix, indent string) {
|
||||||
config := adapter.stream.cfg.configBeforeFrozen
|
config := adapter.stream.cfg.configBeforeFrozen
|
||||||
config.IndentionStep = len(indent)
|
config.IndentionStep = len(indent)
|
||||||
adapter.stream.cfg = config.frozeWithCacheReuse()
|
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEscapeHTML escape html by default, set to false to disable
|
// SetEscapeHTML escape html by default, set to false to disable
|
||||||
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
|
||||||
config := adapter.stream.cfg.configBeforeFrozen
|
config := adapter.stream.cfg.configBeforeFrozen
|
||||||
config.EscapeHTML = escapeHTML
|
config.EscapeHTML = escapeHTML
|
||||||
adapter.stream.cfg = config.frozeWithCacheReuse()
|
adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid reports whether data is a valid JSON encoding.
|
// Valid reports whether data is a valid JSON encoding.
|
||||||
|
@ -2,9 +2,10 @@ package test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_use_number_for_unmarshal(t *testing.T) {
|
func Test_use_number_for_unmarshal(t *testing.T) {
|
||||||
@ -45,3 +46,129 @@ func Test_read_large_number_as_interface(t *testing.T) {
|
|||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal(`123456789123456789123456789`, output)
|
should.Equal(`123456789123456789123456789`, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type caseSensitiveStruct struct {
|
||||||
|
A string `json:"a"`
|
||||||
|
B string `json:"b,omitempty"`
|
||||||
|
C *C `json:"C,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type C struct {
|
||||||
|
D int64 `json:"D,omitempty"`
|
||||||
|
E *E `json:"e,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type E struct {
|
||||||
|
F string `json:"F,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CaseSensitive(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
input string
|
||||||
|
expectedOutput string
|
||||||
|
caseSensitive bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: `{"A":"foo","B":"bar"}`,
|
||||||
|
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||||
|
caseSensitive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","b":"bar"}`,
|
||||||
|
expectedOutput: `{"a":"foo","b":"bar"}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||||
|
expectedOutput: `{"a":"foo","b":"bar","C":{"D":10}}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","B":"bar","c":{"d":10}}`,
|
||||||
|
expectedOutput: `{"a":"foo"}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","C":{"d":10}}`,
|
||||||
|
expectedOutput: `{"a":"foo","C":{}}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","C":{"D":10,"e":{"f":"baz"}}}`,
|
||||||
|
expectedOutput: `{"a":"foo","C":{"D":10,"e":{}}}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||||
|
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"A":"foo","c":{"d":10,"E":{"f":"baz"}}}`,
|
||||||
|
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
|
||||||
|
caseSensitive: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
val := caseSensitiveStruct{}
|
||||||
|
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||||
|
should.Nil(err)
|
||||||
|
|
||||||
|
output, err := jsoniter.MarshalToString(val)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(tc.expectedOutput, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type structWithElevenFields struct {
|
||||||
|
A string `json:"A,omitempty"`
|
||||||
|
B string `json:"B,omitempty"`
|
||||||
|
C string `json:"C,omitempty"`
|
||||||
|
D string `json:"d,omitempty"`
|
||||||
|
E string `json:"e,omitempty"`
|
||||||
|
F string `json:"f,omitempty"`
|
||||||
|
G string `json:"g,omitempty"`
|
||||||
|
H string `json:"h,omitempty"`
|
||||||
|
I string `json:"i,omitempty"`
|
||||||
|
J string `json:"j,omitempty"`
|
||||||
|
K string `json:"k,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
input string
|
||||||
|
expectedOutput string
|
||||||
|
caseSensitive bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||||
|
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"a":"1","b":"2","c":"3","D":"4","E":"5","F":"6"}`,
|
||||||
|
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6"}`,
|
||||||
|
caseSensitive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: `{"A":"1","b":"2","d":"4","E":"5"}`,
|
||||||
|
expectedOutput: `{"A":"1","d":"4"}`,
|
||||||
|
caseSensitive: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
val := structWithElevenFields{}
|
||||||
|
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
|
||||||
|
should.Nil(err)
|
||||||
|
|
||||||
|
output, err := jsoniter.MarshalToString(val)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(tc.expectedOutput, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,3 +56,9 @@ func Test_use_number(t *testing.T) {
|
|||||||
should.Nil(decoder2.Decode(&obj2))
|
should.Nil(decoder2.Decode(&obj2))
|
||||||
should.Equal(json.Number("123"), obj2)
|
should.Equal(json.Number("123"), obj2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_decoder_more(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
decoder := jsoniter.NewDecoder(bytes.NewBufferString("abcde"))
|
||||||
|
should.True(decoder.More())
|
||||||
|
}
|
||||||
|
33
config.go
33
config.go
@ -2,12 +2,13 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/modern-go/concurrent"
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/modern-go/concurrent"
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config customize how the API should behave.
|
// Config customize how the API should behave.
|
||||||
@ -23,6 +24,7 @@ type Config struct {
|
|||||||
OnlyTaggedField bool
|
OnlyTaggedField bool
|
||||||
ValidateJsonRawMessage bool
|
ValidateJsonRawMessage bool
|
||||||
ObjectFieldMustBeSimpleString bool
|
ObjectFieldMustBeSimpleString bool
|
||||||
|
CaseSensitive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// API the public interface of this package.
|
// API the public interface of this package.
|
||||||
@ -72,9 +74,12 @@ type frozenConfig struct {
|
|||||||
disallowUnknownFields bool
|
disallowUnknownFields bool
|
||||||
decoderCache *concurrent.Map
|
decoderCache *concurrent.Map
|
||||||
encoderCache *concurrent.Map
|
encoderCache *concurrent.Map
|
||||||
extensions []Extension
|
encoderExtension Extension
|
||||||
|
decoderExtension Extension
|
||||||
|
extraExtensions []Extension
|
||||||
streamPool *sync.Pool
|
streamPool *sync.Pool
|
||||||
iteratorPool *sync.Pool
|
iteratorPool *sync.Pool
|
||||||
|
caseSensitive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) initCache() {
|
func (cfg *frozenConfig) initCache() {
|
||||||
@ -128,6 +133,7 @@ func (cfg Config) Froze() API {
|
|||||||
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
||||||
onlyTaggedField: cfg.OnlyTaggedField,
|
onlyTaggedField: cfg.OnlyTaggedField,
|
||||||
disallowUnknownFields: cfg.DisallowUnknownFields,
|
disallowUnknownFields: cfg.DisallowUnknownFields,
|
||||||
|
caseSensitive: cfg.CaseSensitive,
|
||||||
}
|
}
|
||||||
api.streamPool = &sync.Pool{
|
api.streamPool = &sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
@ -154,22 +160,21 @@ func (cfg Config) Froze() API {
|
|||||||
if cfg.ValidateJsonRawMessage {
|
if cfg.ValidateJsonRawMessage {
|
||||||
api.validateJsonRawMessage(encoderExtension)
|
api.validateJsonRawMessage(encoderExtension)
|
||||||
}
|
}
|
||||||
if len(encoderExtension) > 0 {
|
api.encoderExtension = encoderExtension
|
||||||
api.extensions = append(api.extensions, encoderExtension)
|
api.decoderExtension = decoderExtension
|
||||||
}
|
|
||||||
if len(decoderExtension) > 0 {
|
|
||||||
api.extensions = append(api.extensions, decoderExtension)
|
|
||||||
}
|
|
||||||
api.configBeforeFrozen = cfg
|
api.configBeforeFrozen = cfg
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Config) frozeWithCacheReuse() *frozenConfig {
|
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
|
||||||
api := getFrozenConfigFromCache(cfg)
|
api := getFrozenConfigFromCache(cfg)
|
||||||
if api != nil {
|
if api != nil {
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
api = cfg.Froze().(*frozenConfig)
|
api = cfg.Froze().(*frozenConfig)
|
||||||
|
for _, extension := range extraExtensions {
|
||||||
|
api.RegisterExtension(extension)
|
||||||
|
}
|
||||||
addFrozenConfigToCache(cfg, api)
|
addFrozenConfigToCache(cfg, api)
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
@ -186,7 +191,7 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
|
|||||||
stream.WriteRaw(string(rawMessage))
|
stream.WriteRaw(string(rawMessage))
|
||||||
}
|
}
|
||||||
}, func(ptr unsafe.Pointer) bool {
|
}, func(ptr unsafe.Pointer) bool {
|
||||||
return false
|
return len(*((*json.RawMessage)(ptr))) == 0
|
||||||
}}
|
}}
|
||||||
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
||||||
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
||||||
@ -215,7 +220,9 @@ func (cfg *frozenConfig) getTagKey() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
|
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
|
||||||
cfg.extensions = append(cfg.extensions, extension)
|
cfg.extraExtensions = append(cfg.extraExtensions, extension)
|
||||||
|
copied := cfg.configBeforeFrozen
|
||||||
|
cfg.configBeforeFrozen = copied
|
||||||
}
|
}
|
||||||
|
|
||||||
type lossyFloat32Encoder struct {
|
type lossyFloat32Encoder struct {
|
||||||
@ -310,7 +317,7 @@ func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]
|
|||||||
}
|
}
|
||||||
newCfg := cfg.configBeforeFrozen
|
newCfg := cfg.configBeforeFrozen
|
||||||
newCfg.IndentionStep = len(indent)
|
newCfg.IndentionStep = len(indent)
|
||||||
return newCfg.frozeWithCacheReuse().Marshal(v)
|
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_customize_type_decoder(t *testing.T) {
|
func Test_customize_type_decoder(t *testing.T) {
|
||||||
@ -98,3 +99,92 @@ func Test_read_custom_interface(t *testing.T) {
|
|||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal("hello", val.Hello())
|
should.Equal("hello", val.Hello())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flow1 = `
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}`
|
||||||
|
|
||||||
|
const flow2 = `
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
{"A":"hello"}
|
||||||
|
`
|
||||||
|
|
||||||
|
type (
|
||||||
|
Type1 struct {
|
||||||
|
A string
|
||||||
|
}
|
||||||
|
|
||||||
|
Type2 struct {
|
||||||
|
A string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *Type2) UnmarshalJSON(data []byte) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Type2) MarshalJSON() ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestType1NoFinalLF(t *testing.T) {
|
||||||
|
reader := bytes.NewReader([]byte(flow1))
|
||||||
|
dec := jsoniter.NewDecoder(reader)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for dec.More() {
|
||||||
|
data := &Type1{}
|
||||||
|
if err := dec.Decode(data); err != nil {
|
||||||
|
t.Errorf("at %v got %v", i, err)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestType1FinalLF(t *testing.T) {
|
||||||
|
reader := bytes.NewReader([]byte(flow2))
|
||||||
|
dec := jsoniter.NewDecoder(reader)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for dec.More() {
|
||||||
|
data := &Type1{}
|
||||||
|
if err := dec.Decode(data); err != nil {
|
||||||
|
t.Errorf("at %v got %v", i, err)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestType2NoFinalLF(t *testing.T) {
|
||||||
|
reader := bytes.NewReader([]byte(flow1))
|
||||||
|
dec := jsoniter.NewDecoder(reader)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for dec.More() {
|
||||||
|
data := &Type2{}
|
||||||
|
if err := dec.Decode(data); err != nil {
|
||||||
|
t.Errorf("at %v got %v", i, err)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestType2FinalLF(t *testing.T) {
|
||||||
|
reader := bytes.NewReader([]byte(flow2))
|
||||||
|
dec := jsoniter.NewDecoder(reader)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for dec.More() {
|
||||||
|
data := &Type2{}
|
||||||
|
if err := dec.Decode(data); err != nil {
|
||||||
|
t.Errorf("at %v got %v", i, err)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -217,6 +217,9 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
|||||||
default:
|
default:
|
||||||
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
||||||
}
|
}
|
||||||
|
if len(str) == 0 {
|
||||||
|
str = "0"
|
||||||
|
}
|
||||||
newIter := iter.Pool().BorrowIterator([]byte(str))
|
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||||
defer iter.Pool().ReturnIterator(newIter)
|
defer iter.Pool().ReturnIterator(newIter)
|
||||||
isFloat := strings.IndexByte(str, '.') != -1
|
isFloat := strings.IndexByte(str, '.') != -1
|
||||||
|
@ -37,6 +37,8 @@ func Test_any_to_int64(t *testing.T) {
|
|||||||
should.Equal(int64(10), val)
|
should.Equal(int64(10), val)
|
||||||
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
|
||||||
should.Equal(int64(10), val)
|
should.Equal(int64(10), val)
|
||||||
|
should.Nil(jsoniter.UnmarshalFromString(`""`, &val))
|
||||||
|
should.Equal(int64(0), val)
|
||||||
|
|
||||||
// bool part
|
// bool part
|
||||||
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
|
||||||
|
@ -2,6 +2,7 @@ package extra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +18,16 @@ type namingStrategyExtension struct {
|
|||||||
|
|
||||||
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||||
for _, binding := range structDescriptor.Fields {
|
for _, binding := range structDescriptor.Fields {
|
||||||
|
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||||
|
if hastag {
|
||||||
|
tagParts := strings.Split(tag, ",")
|
||||||
|
if tagParts[0] == "-" {
|
||||||
|
continue // hidden field
|
||||||
|
}
|
||||||
|
if tagParts[0] != "" {
|
||||||
|
continue // field explicitly named
|
||||||
|
}
|
||||||
|
}
|
||||||
binding.ToNames = []string{extension.translate(binding.Field.Name())}
|
binding.ToNames = []string{extension.translate(binding.Field.Name())}
|
||||||
binding.FromNames = []string{extension.translate(binding.Field.Name())}
|
binding.FromNames = []string{extension.translate(binding.Field.Name())}
|
||||||
}
|
}
|
||||||
|
@ -21,3 +21,30 @@ func Test_lower_case_with_underscores(t *testing.T) {
|
|||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
|
should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_set_naming_strategy_with_overrides(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||||
|
output, err := jsoniter.Marshal(struct {
|
||||||
|
UserName string `json:"UserName"`
|
||||||
|
FirstLanguage string
|
||||||
|
}{
|
||||||
|
UserName: "taowen",
|
||||||
|
FirstLanguage: "Chinese",
|
||||||
|
})
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`{"UserName":"taowen","first_language":"Chinese"}`, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_set_naming_strategy_with_omitempty(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
SetNamingStrategy(LowerCaseWithUnderscores)
|
||||||
|
output, err := jsoniter.Marshal(struct {
|
||||||
|
UserName string
|
||||||
|
FirstLanguage string `json:",omitempty"`
|
||||||
|
}{
|
||||||
|
UserName: "taowen",
|
||||||
|
})
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`{"user_name":"taowen"}`, string(output))
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package extra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,8 +19,36 @@ func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor
|
|||||||
for _, binding := range structDescriptor.Fields {
|
for _, binding := range structDescriptor.Fields {
|
||||||
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
|
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
|
||||||
if isPrivate {
|
if isPrivate {
|
||||||
|
tag, hastag := binding.Field.Tag().Lookup("json")
|
||||||
|
if !hastag {
|
||||||
binding.FromNames = []string{binding.Field.Name()}
|
binding.FromNames = []string{binding.Field.Name()}
|
||||||
binding.ToNames = []string{binding.Field.Name()}
|
binding.ToNames = []string{binding.Field.Name()}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tagParts := strings.Split(tag, ",")
|
||||||
|
names := calcFieldNames(binding.Field.Name(), tagParts[0], tag)
|
||||||
|
binding.FromNames = names
|
||||||
|
binding.ToNames = names
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string {
|
||||||
|
// ignore?
|
||||||
|
if wholeTag == "-" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
// rename?
|
||||||
|
var fieldNames []string
|
||||||
|
if tagProvidedFieldName == "" {
|
||||||
|
fieldNames = []string{originalFieldName}
|
||||||
|
} else {
|
||||||
|
fieldNames = []string{tagProvidedFieldName}
|
||||||
|
}
|
||||||
|
// private?
|
||||||
|
isNotExported := unicode.IsLower(rune(originalFieldName[0]))
|
||||||
|
if isNotExported {
|
||||||
|
fieldNames = []string{}
|
||||||
|
}
|
||||||
|
return fieldNames
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unicode"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadObject read one field from object.
|
// ReadObject read one field from object.
|
||||||
@ -60,7 +60,7 @@ func (iter *Iterator) readFieldHash() int64 {
|
|||||||
if b == '\\' {
|
if b == '\\' {
|
||||||
iter.head = i
|
iter.head = i
|
||||||
for _, b := range iter.readStringSlowPath() {
|
for _, b := range iter.readStringSlowPath() {
|
||||||
if 'A' <= b && b <= 'Z' {
|
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||||
b += 'a' - 'A'
|
b += 'a' - 'A'
|
||||||
}
|
}
|
||||||
hash ^= int64(b)
|
hash ^= int64(b)
|
||||||
@ -82,7 +82,7 @@ func (iter *Iterator) readFieldHash() int64 {
|
|||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
if 'A' <= b && b <= 'Z' {
|
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
|
||||||
b += 'a' - 'A'
|
b += 'a' - 'A'
|
||||||
}
|
}
|
||||||
hash ^= int64(b)
|
hash ^= int64(b)
|
||||||
@ -95,10 +95,13 @@ func (iter *Iterator) readFieldHash() int64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcHash(str string) int64 {
|
func calcHash(str string, caseSensitive bool) int64 {
|
||||||
|
if !caseSensitive {
|
||||||
|
str = strings.ToLower(str)
|
||||||
|
}
|
||||||
hash := int64(0x811c9dc5)
|
hash := int64(0x811c9dc5)
|
||||||
for _, b := range str {
|
for _, b := range []byte(str) {
|
||||||
hash ^= int64(unicode.ToLower(b))
|
hash ^= int64(b)
|
||||||
hash *= 0x1000193
|
hash *= 0x1000193
|
||||||
}
|
}
|
||||||
return int64(hash)
|
return int64(hash)
|
||||||
|
@ -3,9 +3,10 @@ package misc_tests
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_empty_array(t *testing.T) {
|
func Test_empty_array(t *testing.T) {
|
||||||
@ -168,6 +169,17 @@ func Test_decode_byte_array_from_base64(t *testing.T) {
|
|||||||
should.Equal([]byte{1, 2, 3}, data)
|
should.Equal([]byte{1, 2, 3}, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_decode_byte_array_from_base64_with_newlines(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
data := []byte{}
|
||||||
|
err := json.Unmarshal([]byte(`"A\rQ\nID"`), &data)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal([]byte{1, 2, 3}, data)
|
||||||
|
err = jsoniter.Unmarshal([]byte(`"A\rQ\nID"`), &data)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal([]byte{1, 2, 3}, data)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_decode_byte_array_from_array(t *testing.T) {
|
func Test_decode_byte_array_from_array(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
data := []byte{}
|
data := []byte{}
|
||||||
|
1
pool.go
1
pool.go
@ -23,6 +23,7 @@ func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
|
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
|
||||||
|
stream.out = nil
|
||||||
stream.Error = nil
|
stream.Error = nil
|
||||||
stream.Attachment = nil
|
stream.Attachment = nil
|
||||||
cfg.streamPool.Put(stream)
|
cfg.streamPool.Put(stream)
|
||||||
|
17
reflect.go
17
reflect.go
@ -2,9 +2,10 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValDecoder is an internal type registered to cache as needed.
|
// ValDecoder is an internal type registered to cache as needed.
|
||||||
@ -40,6 +41,14 @@ type ctx struct {
|
|||||||
decoders map[reflect2.Type]ValDecoder
|
decoders map[reflect2.Type]ValDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *ctx) caseSensitive() bool {
|
||||||
|
if b.frozenConfig == nil {
|
||||||
|
// default is case-insensitive
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return b.frozenConfig.caseSensitive
|
||||||
|
}
|
||||||
|
|
||||||
func (b *ctx) append(prefix string) *ctx {
|
func (b *ctx) append(prefix string) *ctx {
|
||||||
return &ctx{
|
return &ctx{
|
||||||
frozenConfig: b.frozenConfig,
|
frozenConfig: b.frozenConfig,
|
||||||
@ -111,7 +120,8 @@ func decoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
decoder = extension.DecorateDecoder(typ, decoder)
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder)
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
decoder = extension.DecorateDecoder(typ, decoder)
|
||||||
}
|
}
|
||||||
return decoder
|
return decoder
|
||||||
@ -213,7 +223,8 @@ func encoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
encoder = extension.DecorateEncoder(typ, encoder)
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder)
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
encoder = extension.DecorateEncoder(typ, encoder)
|
||||||
}
|
}
|
||||||
return encoder
|
return encoder
|
||||||
|
@ -246,7 +246,8 @@ func getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
decoder = extension.DecorateDecoder(typ, decoder)
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder)
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
decoder = extension.DecorateDecoder(typ, decoder)
|
decoder = extension.DecorateDecoder(typ, decoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,14 +260,18 @@ func _getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
return decoder
|
return decoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
decoder := ctx.decoderExtension.CreateDecoder(typ)
|
||||||
|
if decoder != nil {
|
||||||
|
return decoder
|
||||||
|
}
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
decoder := extension.CreateDecoder(typ)
|
decoder := extension.CreateDecoder(typ)
|
||||||
if decoder != nil {
|
if decoder != nil {
|
||||||
return decoder
|
return decoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typeName := typ.String()
|
typeName := typ.String()
|
||||||
decoder := typeDecoders[typeName]
|
decoder = typeDecoders[typeName]
|
||||||
if decoder != nil {
|
if decoder != nil {
|
||||||
return decoder
|
return decoder
|
||||||
}
|
}
|
||||||
@ -286,7 +291,8 @@ func getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
encoder = extension.DecorateEncoder(typ, encoder)
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder)
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
encoder = extension.DecorateEncoder(typ, encoder)
|
encoder = extension.DecorateEncoder(typ, encoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,14 +306,18 @@ func _getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|||||||
return encoder
|
return encoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
encoder := ctx.encoderExtension.CreateEncoder(typ)
|
||||||
|
if encoder != nil {
|
||||||
|
return encoder
|
||||||
|
}
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
encoder := extension.CreateEncoder(typ)
|
encoder := extension.CreateEncoder(typ)
|
||||||
if encoder != nil {
|
if encoder != nil {
|
||||||
return encoder
|
return encoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typeName := typ.String()
|
typeName := typ.String()
|
||||||
encoder := typeEncoders[typeName]
|
encoder = typeEncoders[typeName]
|
||||||
if encoder != nil {
|
if encoder != nil {
|
||||||
return encoder
|
return encoder
|
||||||
}
|
}
|
||||||
@ -393,7 +403,9 @@ func createStructDescriptor(ctx *ctx, typ reflect2.Type, bindings []*Binding, em
|
|||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
extension.UpdateStructDescriptor(structDescriptor)
|
extension.UpdateStructDescriptor(structDescriptor)
|
||||||
}
|
}
|
||||||
for _, extension := range ctx.extensions {
|
ctx.encoderExtension.UpdateStructDescriptor(structDescriptor)
|
||||||
|
ctx.decoderExtension.UpdateStructDescriptor(structDescriptor)
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
extension.UpdateStructDescriptor(structDescriptor)
|
extension.UpdateStructDescriptor(structDescriptor)
|
||||||
}
|
}
|
||||||
processTags(structDescriptor, ctx.frozenConfig)
|
processTags(structDescriptor, ctx.frozenConfig)
|
||||||
|
@ -39,7 +39,11 @@ func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
|
func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
|
||||||
for _, extension := range ctx.extensions {
|
decoder := ctx.decoderExtension.CreateMapKeyDecoder(typ)
|
||||||
|
if decoder != nil {
|
||||||
|
return decoder
|
||||||
|
}
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
decoder := extension.CreateMapKeyDecoder(typ)
|
decoder := extension.CreateMapKeyDecoder(typ)
|
||||||
if decoder != nil {
|
if decoder != nil {
|
||||||
return decoder
|
return decoder
|
||||||
@ -77,7 +81,11 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
|
func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
|
||||||
for _, extension := range ctx.extensions {
|
encoder := ctx.encoderExtension.CreateMapKeyEncoder(typ)
|
||||||
|
if encoder != nil {
|
||||||
|
return encoder
|
||||||
|
}
|
||||||
|
for _, extension := range ctx.extraExtensions {
|
||||||
encoder := extension.CreateMapKeyEncoder(typ)
|
encoder := extension.CreateMapKeyEncoder(typ)
|
||||||
if encoder != nil {
|
if encoder != nil {
|
||||||
return encoder
|
return encoder
|
||||||
|
@ -2,10 +2,11 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ptrSize = 32 << uintptr(^uintptr(0)>>63)
|
const ptrSize = 32 << uintptr(^uintptr(0)>>63)
|
||||||
@ -416,16 +417,11 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
}
|
}
|
||||||
switch iter.WhatIsNext() {
|
switch iter.WhatIsNext() {
|
||||||
case StringValue:
|
case StringValue:
|
||||||
encoding := base64.StdEncoding
|
src := iter.ReadString()
|
||||||
src := iter.SkipAndReturnBytes()
|
dst, err := base64.StdEncoding.DecodeString(src)
|
||||||
src = src[1 : len(src)-1]
|
|
||||||
decodedLen := encoding.DecodedLen(len(src))
|
|
||||||
dst := make([]byte, decodedLen)
|
|
||||||
len, err := encoding.Decode(dst, src)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
iter.ReportError("decode base64", err.Error())
|
iter.ReportError("decode base64", err.Error())
|
||||||
} else {
|
} else {
|
||||||
dst = dst[:len]
|
|
||||||
codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst))
|
codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst))
|
||||||
}
|
}
|
||||||
case ArrayValue:
|
case ArrayValue:
|
||||||
|
@ -2,10 +2,11 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
|
func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
|
||||||
@ -31,6 +32,15 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
for k, binding := range bindings {
|
for k, binding := range bindings {
|
||||||
fields[k] = binding.Decoder.(*structFieldDecoder)
|
fields[k] = binding.Decoder.(*structFieldDecoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !ctx.caseSensitive() {
|
||||||
|
for k, binding := range bindings {
|
||||||
|
if _, found := fields[strings.ToLower(k)]; !found {
|
||||||
|
fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return createStructDecoder(ctx, typ, fields)
|
return createStructDecoder(ctx, typ, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,12 +51,13 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
knownHash := map[int64]struct{}{
|
knownHash := map[int64]struct{}{
|
||||||
0: {},
|
0: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(fields) {
|
switch len(fields) {
|
||||||
case 0:
|
case 0:
|
||||||
return &skipObjectDecoder{typ}
|
return &skipObjectDecoder{typ}
|
||||||
case 1:
|
case 1:
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -60,7 +71,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder1 *structFieldDecoder
|
var fieldDecoder1 *structFieldDecoder
|
||||||
var fieldDecoder2 *structFieldDecoder
|
var fieldDecoder2 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -83,7 +94,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder2 *structFieldDecoder
|
var fieldDecoder2 *structFieldDecoder
|
||||||
var fieldDecoder3 *structFieldDecoder
|
var fieldDecoder3 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -114,7 +125,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder3 *structFieldDecoder
|
var fieldDecoder3 *structFieldDecoder
|
||||||
var fieldDecoder4 *structFieldDecoder
|
var fieldDecoder4 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -151,7 +162,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder4 *structFieldDecoder
|
var fieldDecoder4 *structFieldDecoder
|
||||||
var fieldDecoder5 *structFieldDecoder
|
var fieldDecoder5 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -194,7 +205,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder5 *structFieldDecoder
|
var fieldDecoder5 *structFieldDecoder
|
||||||
var fieldDecoder6 *structFieldDecoder
|
var fieldDecoder6 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -243,7 +254,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder6 *structFieldDecoder
|
var fieldDecoder6 *structFieldDecoder
|
||||||
var fieldDecoder7 *structFieldDecoder
|
var fieldDecoder7 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -298,7 +309,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder7 *structFieldDecoder
|
var fieldDecoder7 *structFieldDecoder
|
||||||
var fieldDecoder8 *structFieldDecoder
|
var fieldDecoder8 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -359,7 +370,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder8 *structFieldDecoder
|
var fieldDecoder8 *structFieldDecoder
|
||||||
var fieldDecoder9 *structFieldDecoder
|
var fieldDecoder9 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -426,7 +437,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
|
|||||||
var fieldDecoder9 *structFieldDecoder
|
var fieldDecoder9 *structFieldDecoder
|
||||||
var fieldDecoder10 *structFieldDecoder
|
var fieldDecoder10 *structFieldDecoder
|
||||||
for fieldName, fieldDecoder := range fields {
|
for fieldName, fieldDecoder := range fields {
|
||||||
fieldHash := calcHash(fieldName)
|
fieldHash := calcHash(fieldName, ctx.caseSensitive())
|
||||||
_, known := knownHash[fieldHash]
|
_, known := knownHash[fieldHash]
|
||||||
if known {
|
if known {
|
||||||
return &generalStructDecoder{typ, fields, false}
|
return &generalStructDecoder{typ, fields, false}
|
||||||
@ -489,13 +500,16 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
decoder.decodeOneField(ptr, iter)
|
var c byte
|
||||||
for iter.nextToken() == ',' {
|
for c = ','; c == ','; c = iter.nextToken() {
|
||||||
decoder.decodeOneField(ptr, iter)
|
decoder.decodeOneField(ptr, iter)
|
||||||
}
|
}
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
|
iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error())
|
||||||
}
|
}
|
||||||
|
if c != '}' {
|
||||||
|
iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
|
func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
@ -505,13 +519,13 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It
|
|||||||
fieldBytes := iter.ReadStringAsSlice()
|
fieldBytes := iter.ReadStringAsSlice()
|
||||||
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
field = *(*string)(unsafe.Pointer(&fieldBytes))
|
||||||
fieldDecoder = decoder.fields[field]
|
fieldDecoder = decoder.fields[field]
|
||||||
if fieldDecoder == nil {
|
if fieldDecoder == nil && !iter.cfg.caseSensitive {
|
||||||
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
field = iter.ReadString()
|
field = iter.ReadString()
|
||||||
fieldDecoder = decoder.fields[field]
|
fieldDecoder = decoder.fields[field]
|
||||||
if fieldDecoder == nil {
|
if fieldDecoder == nil && !iter.cfg.caseSensitive {
|
||||||
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
fieldDecoder = decoder.fields[strings.ToLower(field)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,9 @@ func init() {
|
|||||||
(*struct {
|
(*struct {
|
||||||
Field bool `json:",omitempty,string"`
|
Field bool `json:",omitempty,string"`
|
||||||
})(nil),
|
})(nil),
|
||||||
|
(*struct {
|
||||||
|
Field bool `json:"中文"`
|
||||||
|
})(nil),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
value_tests/error_test.go
Normal file
36
value_tests/error_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/json-iterator/go"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_errorInput(t *testing.T) {
|
||||||
|
for _, testCase := range unmarshalCases {
|
||||||
|
if testCase.obj != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
valType := reflect.TypeOf(testCase.ptr).Elem()
|
||||||
|
t.Run(valType.String(), func(t *testing.T) {
|
||||||
|
for _, data := range []string{
|
||||||
|
`x`,
|
||||||
|
`n`,
|
||||||
|
`nul`,
|
||||||
|
`{x}`,
|
||||||
|
`{"x"}`,
|
||||||
|
`{"x": "y"x}`,
|
||||||
|
`{"x": "y"`,
|
||||||
|
`{"x": "y", "a"}`,
|
||||||
|
`[`,
|
||||||
|
`[{"x": "y"}`,
|
||||||
|
} {
|
||||||
|
ptrVal := reflect.New(valType)
|
||||||
|
ptr := ptrVal.Interface()
|
||||||
|
err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(data), ptr)
|
||||||
|
require.Error(t, err, "on input %q", data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,18 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
marshalCases = append(marshalCases,
|
marshalCases = append(marshalCases,
|
||||||
json.RawMessage("{}"),
|
json.RawMessage("{}"),
|
||||||
|
selectedMarshalCase{struct {
|
||||||
|
Env string `json:"env"`
|
||||||
|
Extra json.RawMessage `json:"extra,omitempty"`
|
||||||
|
}{
|
||||||
|
Env: "jfdk",
|
||||||
|
}},
|
||||||
)
|
)
|
||||||
unmarshalCases = append(unmarshalCases, unmarshalCase{
|
unmarshalCases = append(unmarshalCases, unmarshalCase{
|
||||||
ptr: (*json.RawMessage)(nil),
|
ptr: (*json.RawMessage)(nil),
|
||||||
|
@ -20,5 +20,8 @@ func init() {
|
|||||||
}, unmarshalCase{
|
}, unmarshalCase{
|
||||||
ptr: (*[]byte)(nil),
|
ptr: (*[]byte)(nil),
|
||||||
input: `"aGVsbG8="`,
|
input: `"aGVsbG8="`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*[]byte)(nil),
|
||||||
|
input: `"c3ViamVjdHM\/X2Q9MQ=="`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,43 @@ func init() {
|
|||||||
d *time.Timer
|
d *time.Timer
|
||||||
})(nil),
|
})(nil),
|
||||||
input: `{"a": 444, "b":"bad", "C":256, "d":{"not":"a timer"}}`,
|
input: `{"a": 444, "b":"bad", "C":256, "d":{"not":"a timer"}}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*struct {
|
||||||
|
A string
|
||||||
|
B string
|
||||||
|
C string
|
||||||
|
D string
|
||||||
|
E string
|
||||||
|
F string
|
||||||
|
G string
|
||||||
|
H string
|
||||||
|
I string
|
||||||
|
J string
|
||||||
|
K string
|
||||||
|
})(nil),
|
||||||
|
input: `{"a":"1","b":"2","c":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*struct {
|
||||||
|
T float64 `json:"T"`
|
||||||
|
})(nil),
|
||||||
|
input: `{"t":10.0}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*struct {
|
||||||
|
T float64 `json:"T"`
|
||||||
|
})(nil),
|
||||||
|
input: `{"T":10.0}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*struct {
|
||||||
|
T float64 `json:"t"`
|
||||||
|
})(nil),
|
||||||
|
input: `{"T":10.0}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*struct {
|
||||||
|
KeyString string `json:"key_string"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Asks [][2]float64 `json:"asks"`
|
||||||
|
})(nil),
|
||||||
|
input: `{"key_string": "KEYSTRING","type": "TYPE","asks": [[1e+66,1]]}`,
|
||||||
})
|
})
|
||||||
marshalCases = append(marshalCases,
|
marshalCases = append(marshalCases,
|
||||||
struct {
|
struct {
|
||||||
|
Reference in New Issue
Block a user