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
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
03217c3e97 | |||
908eaed151 | |||
eec24895fe | |||
1ba732a07d | |||
819acad769 | |||
695ec2b83b | |||
028e2ef2bd | |||
976454858b | |||
27518f6661 | |||
94869abf43 | |||
459f0e30ae | |||
0039f4ac3d | |||
fb5614a4ca | |||
f71b9090aa | |||
08047c174c | |||
68347ec4d6 | |||
0fd91468bb | |||
1bc9828b4f | |||
24c3d57281 | |||
0ff49de124 | |||
5bc9320502 | |||
f64ce68b6e | |||
2d42ff74dd | |||
3a023a5fbc | |||
16aef10b2b | |||
ae4c002f78 | |||
e4aa2ec063 | |||
d05f387f50 | |||
a9403d25cd | |||
05d041de10 | |||
5916df66b3 | |||
2433035e51 | |||
6dfc0bf2dd | |||
b9be8dd373 | |||
b8d78b6aaf | |||
7109b5e7dd | |||
4cc76529e8 | |||
c5ddac9dc3 | |||
f76d712086 | |||
1624edc445 | |||
5d789e5e02 | |||
0260c89b54 | |||
10a568c511 |
@ -10,10 +10,6 @@ A high-performance 100% compatible drop-in replacement of "encoding/json"
|
|||||||
|
|
||||||
You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go)
|
You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go)
|
||||||
|
|
||||||
```
|
|
||||||
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
|
|
||||||
```
|
|
||||||
|
|
||||||
# Benchmark
|
# Benchmark
|
||||||
|
|
||||||

|

|
||||||
|
18
adapter.go
18
adapter.go
@ -16,7 +16,7 @@ func Unmarshal(data []byte, v interface{}) error {
|
|||||||
return ConfigDefault.Unmarshal(data, v)
|
return ConfigDefault.Unmarshal(data, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalFromString convenient method to read from string instead of []byte
|
// UnmarshalFromString is a convenient method to read from string instead of []byte
|
||||||
func UnmarshalFromString(str string, v interface{}) error {
|
func UnmarshalFromString(str string, v interface{}) error {
|
||||||
return ConfigDefault.UnmarshalFromString(str, v)
|
return ConfigDefault.UnmarshalFromString(str, v)
|
||||||
}
|
}
|
||||||
@ -81,10 +81,12 @@ func (adapter *Decoder) More() bool {
|
|||||||
if iter.Error != nil {
|
if iter.Error != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if iter.head != iter.tail {
|
c := iter.nextToken()
|
||||||
return true
|
if c == 0 {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return iter.loadMore()
|
iter.unreadByte()
|
||||||
|
return c != ']' && c != '}'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffered remaining buffer
|
// Buffered remaining buffer
|
||||||
@ -98,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
|
||||||
@ -107,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
|
||||||
@ -132,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.
|
||||||
|
4
any.go
4
any.go
@ -312,6 +312,10 @@ func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
|
|
||||||
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
any := *(*Any)(ptr)
|
any := *(*Any)(ptr)
|
||||||
|
if any == nil {
|
||||||
|
stream.WriteNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
any.WriteTo(stream)
|
any.WriteTo(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +82,8 @@ func Test_read_float_to_any(t *testing.T) {
|
|||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
any := jsoniter.WrapFloat64(12.3)
|
any := jsoniter.WrapFloat64(12.3)
|
||||||
anyFloat64 := float64(12.3)
|
anyFloat64 := float64(12.3)
|
||||||
//negaAnyFloat64 := float64(-1.1)
|
|
||||||
any2 := jsoniter.WrapFloat64(-1.1)
|
any2 := jsoniter.WrapFloat64(-1.1)
|
||||||
should.Equal(float64(12.3), any.ToFloat64())
|
should.Equal(float64(12.3), any.ToFloat64())
|
||||||
//should.Equal("12.3", any.ToString())
|
|
||||||
should.True(any.ToBool())
|
should.True(any.ToBool())
|
||||||
should.Equal(float32(anyFloat64), any.ToFloat32())
|
should.Equal(float32(anyFloat64), any.ToFloat32())
|
||||||
should.Equal(int(anyFloat64), any.ToInt())
|
should.Equal(int(anyFloat64), any.ToInt())
|
||||||
|
@ -118,6 +118,4 @@ func Test_object_wrapper_any_get_all(t *testing.T) {
|
|||||||
should.Contains(any.Keys(), "Field1")
|
should.Contains(any.Keys(), "Field1")
|
||||||
should.Contains(any.Keys(), "Field2")
|
should.Contains(any.Keys(), "Field2")
|
||||||
should.NotContains(any.Keys(), "Field3")
|
should.NotContains(any.Keys(), "Field3")
|
||||||
|
|
||||||
//should.Contains(any.GetObject()["Field1"].GetArray()[0], 1)
|
|
||||||
}
|
}
|
||||||
|
@ -8,24 +8,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var stringConvertMap = map[string]string{
|
var stringConvertMap = map[string]string{
|
||||||
"null": "",
|
"null": "",
|
||||||
"321.1": "321.1",
|
"321.1": "321.1",
|
||||||
`"1.1"`: "1.1",
|
`"1.1"`: "1.1",
|
||||||
`"-123.1"`: "-123.1",
|
`"-123.1"`: "-123.1",
|
||||||
"0.0": "0.0",
|
"0.0": "0.0",
|
||||||
"0": "0",
|
"0": "0",
|
||||||
`"0"`: "0",
|
`"0"`: "0",
|
||||||
`"0.0"`: "0.0",
|
`"0.0"`: "0.0",
|
||||||
`"00.0"`: "00.0",
|
`"00.0"`: "00.0",
|
||||||
"true": "true",
|
"true": "true",
|
||||||
"false": "false",
|
"false": "false",
|
||||||
`"true"`: "true",
|
`"true"`: "true",
|
||||||
`"false"`: "false",
|
`"false"`: "false",
|
||||||
`"true123"`: "true123",
|
`"true123"`: "true123",
|
||||||
`"+1"`: "+1",
|
`"+1"`: "+1",
|
||||||
"[]": "[]",
|
"[]": "[]",
|
||||||
"[1,2]": "[1,2]",
|
"[1,2]": "[1,2]",
|
||||||
"{}": "{}",
|
"{}": "{}",
|
||||||
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,3 +172,58 @@ func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
|
|||||||
should.Equal(tc.expectedOutput, output)
|
should.Equal(tc.expectedOutput, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type onlyTaggedFieldStruct struct {
|
||||||
|
A string `json:"a"`
|
||||||
|
B string
|
||||||
|
FSimpl F `json:"f_simpl"`
|
||||||
|
ISimpl I
|
||||||
|
FPtr *F `json:"f_ptr"`
|
||||||
|
IPtr *I
|
||||||
|
F
|
||||||
|
*I
|
||||||
|
}
|
||||||
|
|
||||||
|
type F struct {
|
||||||
|
G string `json:"g"`
|
||||||
|
H string
|
||||||
|
}
|
||||||
|
|
||||||
|
type I struct {
|
||||||
|
J string `json:"j"`
|
||||||
|
K string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_OnlyTaggedField(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
|
||||||
|
obj := onlyTaggedFieldStruct{
|
||||||
|
A: "a",
|
||||||
|
B: "b",
|
||||||
|
FSimpl: F{G: "g", H: "h"},
|
||||||
|
ISimpl: I{J: "j", K: "k"},
|
||||||
|
FPtr: &F{G: "g", H: "h"},
|
||||||
|
IPtr: &I{J: "j", K: "k"},
|
||||||
|
F: F{G: "g", H: "h"},
|
||||||
|
I: &I{J: "j", K: "k"},
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := jsoniter.Config{OnlyTaggedField: true}.Froze().Marshal(obj)
|
||||||
|
should.Nil(err)
|
||||||
|
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
err = jsoniter.Unmarshal(output, &m)
|
||||||
|
should.Nil(err)
|
||||||
|
|
||||||
|
should.Equal(map[string]interface{}{
|
||||||
|
"a": "a",
|
||||||
|
"f_simpl": map[string]interface{}{
|
||||||
|
"g": "g",
|
||||||
|
},
|
||||||
|
"f_ptr": map[string]interface{}{
|
||||||
|
"g": "g",
|
||||||
|
},
|
||||||
|
"g": "g",
|
||||||
|
"j": "j",
|
||||||
|
}, m)
|
||||||
|
}
|
||||||
|
47
api_tests/marshal_json_escape_test.go
Normal file
47
api_tests/marshal_json_escape_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var marshalConfig = jsoniter.Config{
|
||||||
|
EscapeHTML: false,
|
||||||
|
SortMapKeys: true,
|
||||||
|
ValidateJsonRawMessage: true,
|
||||||
|
}.Froze()
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Bar interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) MarshalJSON() ([]byte, error) {
|
||||||
|
return marshalConfig.Marshal(&c.Bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeEscape(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
|
||||||
|
container := &Container{
|
||||||
|
Bar: []string{"123<ab>", "ooo"},
|
||||||
|
}
|
||||||
|
out, err := marshalConfig.Marshal(container)
|
||||||
|
should.Nil(err)
|
||||||
|
bufout := string(out)
|
||||||
|
|
||||||
|
var stdbuf bytes.Buffer
|
||||||
|
stdenc := json.NewEncoder(&stdbuf)
|
||||||
|
stdenc.SetEscapeHTML(false)
|
||||||
|
err = stdenc.Encode(container)
|
||||||
|
should.Nil(err)
|
||||||
|
stdout := string(stdbuf.Bytes())
|
||||||
|
if stdout[len(stdout)-1:] == "\n" {
|
||||||
|
stdout = stdout[:len(stdout)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
should.Equal(stdout, bufout)
|
||||||
|
}
|
36
api_tests/marshal_json_test.go
Normal file
36
api_tests/marshal_json_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/json-iterator/go"
|
||||||
|
"testing"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
Bar interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Foo) MarshalJSON() ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := json.NewEncoder(&buf).Encode(f.Bar)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Standard Encoder has trailing newline.
|
||||||
|
func TestEncodeMarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
|
foo := Foo {
|
||||||
|
Bar: 123,
|
||||||
|
}
|
||||||
|
should := require.New(t)
|
||||||
|
var buf, stdbuf bytes.Buffer
|
||||||
|
enc := jsoniter.ConfigCompatibleWithStandardLibrary.NewEncoder(&buf)
|
||||||
|
enc.Encode(foo)
|
||||||
|
stdenc := json.NewEncoder(&stdbuf)
|
||||||
|
stdenc.Encode(foo)
|
||||||
|
should.Equal(stdbuf.Bytes(), buf.Bytes())
|
||||||
|
}
|
25
config.go
25
config.go
@ -74,7 +74,9 @@ 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
|
caseSensitive bool
|
||||||
@ -158,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
|
||||||
}
|
}
|
||||||
@ -190,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
|
||||||
@ -219,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 {
|
||||||
@ -314,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 {
|
||||||
|
@ -95,7 +95,7 @@ func ExampleGet() {
|
|||||||
// Crimson
|
// Crimson
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMapKey() {
|
func ExampleMyKey() {
|
||||||
hello := MyKey("hello")
|
hello := MyKey("hello")
|
||||||
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
|
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
|
||||||
fmt.Println(string(output))
|
fmt.Println(string(output))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -153,7 +153,7 @@ func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iter
|
|||||||
}
|
}
|
||||||
b4 := rawBytes[i+3]
|
b4 := rawBytes[i+3]
|
||||||
b5 := rawBytes[i+4]
|
b5 := rawBytes[i+4]
|
||||||
i = i + 4
|
i += 4
|
||||||
b = readHex(iter, b4, b5)
|
b = readHex(iter, b4, b5)
|
||||||
}
|
}
|
||||||
bytes = append(bytes, b)
|
bytes = append(bytes, b)
|
||||||
@ -178,7 +178,7 @@ func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
|
|||||||
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
|
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
ret = ret * 16
|
ret *= 16
|
||||||
if b2 >= '0' && b2 <= '9' {
|
if b2 >= '0' && b2 <= '9' {
|
||||||
ret = b2 - '0'
|
ret = b2 - '0'
|
||||||
} else if b2 >= 'a' && b2 <= 'f' {
|
} else if b2 >= 'a' && b2 <= 'f' {
|
||||||
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module github.com/json-iterator/go
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/google/gofuzz v1.0.0
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
|
||||||
|
github.com/stretchr/testify v1.3.0
|
||||||
|
)
|
14
go.sum
Normal file
14
go.sum
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
27
iter.go
27
iter.go
@ -74,6 +74,7 @@ type Iterator struct {
|
|||||||
buf []byte
|
buf []byte
|
||||||
head int
|
head int
|
||||||
tail int
|
tail int
|
||||||
|
depth int
|
||||||
captureStartedAt int
|
captureStartedAt int
|
||||||
captured []byte
|
captured []byte
|
||||||
Error error
|
Error error
|
||||||
@ -88,6 +89,7 @@ func NewIterator(cfg API) *Iterator {
|
|||||||
buf: nil,
|
buf: nil,
|
||||||
head: 0,
|
head: 0,
|
||||||
tail: 0,
|
tail: 0,
|
||||||
|
depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +101,7 @@ func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
|
|||||||
buf: make([]byte, bufSize),
|
buf: make([]byte, bufSize),
|
||||||
head: 0,
|
head: 0,
|
||||||
tail: 0,
|
tail: 0,
|
||||||
|
depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +113,7 @@ func ParseBytes(cfg API, input []byte) *Iterator {
|
|||||||
buf: input,
|
buf: input,
|
||||||
head: 0,
|
head: 0,
|
||||||
tail: len(input),
|
tail: len(input),
|
||||||
|
depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +132,7 @@ func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
|||||||
iter.reader = reader
|
iter.reader = reader
|
||||||
iter.head = 0
|
iter.head = 0
|
||||||
iter.tail = 0
|
iter.tail = 0
|
||||||
|
iter.depth = 0
|
||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +142,7 @@ func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
|||||||
iter.buf = input
|
iter.buf = input
|
||||||
iter.head = 0
|
iter.head = 0
|
||||||
iter.tail = len(input)
|
iter.tail = len(input)
|
||||||
|
iter.depth = 0
|
||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,3 +326,24 @@ func (iter *Iterator) Read() interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
|
||||||
|
const maxDepth = 10000
|
||||||
|
|
||||||
|
func (iter *Iterator) incrementDepth() (success bool) {
|
||||||
|
iter.depth++
|
||||||
|
if iter.depth <= maxDepth {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
iter.ReportError("incrementDepth", "exceeded max depth")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) decrementDepth() (success bool) {
|
||||||
|
iter.depth--
|
||||||
|
if iter.depth >= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
iter.ReportError("decrementDepth", "unexpected negative nesting")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -28,26 +28,32 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
|||||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '[' {
|
if c == '[' {
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c != ']' {
|
if c != ']' {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
if !callback(iter) {
|
if !callback(iter) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
for c == ',' {
|
for c == ',' {
|
||||||
if !callback(iter) {
|
if !callback(iter) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
}
|
}
|
||||||
if c != ']' {
|
if c != ']' {
|
||||||
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
|
iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c}))
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
if c == 'n' {
|
if c == 'n' {
|
||||||
iter.skipThreeBytes('u', 'l', 'l')
|
iter.skipThreeBytes('u', 'l', 'l')
|
||||||
|
@ -77,14 +77,12 @@ func (iter *Iterator) ReadFloat32() (ret float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||||
value := uint64(0)
|
|
||||||
c := byte(' ')
|
|
||||||
i := iter.head
|
i := iter.head
|
||||||
// first char
|
// first char
|
||||||
if i == iter.tail {
|
if i == iter.tail {
|
||||||
return iter.readFloat32SlowPath()
|
return iter.readFloat32SlowPath()
|
||||||
}
|
}
|
||||||
c = iter.buf[i]
|
c := iter.buf[i]
|
||||||
i++
|
i++
|
||||||
ind := floatDigits[c]
|
ind := floatDigits[c]
|
||||||
switch ind {
|
switch ind {
|
||||||
@ -107,7 +105,7 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value = uint64(ind)
|
value := uint64(ind)
|
||||||
// chars before dot
|
// chars before dot
|
||||||
non_decimal_loop:
|
non_decimal_loop:
|
||||||
for ; i < iter.tail; i++ {
|
for ; i < iter.tail; i++ {
|
||||||
@ -145,9 +143,7 @@ non_decimal_loop:
|
|||||||
}
|
}
|
||||||
// too many decimal places
|
// too many decimal places
|
||||||
return iter.readFloat32SlowPath()
|
return iter.readFloat32SlowPath()
|
||||||
case invalidCharForNumber:
|
case invalidCharForNumber, dotInNumber:
|
||||||
fallthrough
|
|
||||||
case dotInNumber:
|
|
||||||
return iter.readFloat32SlowPath()
|
return iter.readFloat32SlowPath()
|
||||||
}
|
}
|
||||||
decimalPlaces++
|
decimalPlaces++
|
||||||
@ -218,14 +214,12 @@ func (iter *Iterator) ReadFloat64() (ret float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||||
value := uint64(0)
|
|
||||||
c := byte(' ')
|
|
||||||
i := iter.head
|
i := iter.head
|
||||||
// first char
|
// first char
|
||||||
if i == iter.tail {
|
if i == iter.tail {
|
||||||
return iter.readFloat64SlowPath()
|
return iter.readFloat64SlowPath()
|
||||||
}
|
}
|
||||||
c = iter.buf[i]
|
c := iter.buf[i]
|
||||||
i++
|
i++
|
||||||
ind := floatDigits[c]
|
ind := floatDigits[c]
|
||||||
switch ind {
|
switch ind {
|
||||||
@ -248,7 +242,7 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value = uint64(ind)
|
value := uint64(ind)
|
||||||
// chars before dot
|
// chars before dot
|
||||||
non_decimal_loop:
|
non_decimal_loop:
|
||||||
for ; i < iter.tail; i++ {
|
for ; i < iter.tail; i++ {
|
||||||
@ -286,9 +280,7 @@ non_decimal_loop:
|
|||||||
}
|
}
|
||||||
// too many decimal places
|
// too many decimal places
|
||||||
return iter.readFloat64SlowPath()
|
return iter.readFloat64SlowPath()
|
||||||
case invalidCharForNumber:
|
case invalidCharForNumber, dotInNumber:
|
||||||
fallthrough
|
|
||||||
case dotInNumber:
|
|
||||||
return iter.readFloat64SlowPath()
|
return iter.readFloat64SlowPath()
|
||||||
}
|
}
|
||||||
decimalPlaces++
|
decimalPlaces++
|
||||||
|
@ -2,7 +2,7 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unicode"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadObject read one field from object.
|
// ReadObject read one field from object.
|
||||||
@ -96,13 +96,12 @@ func (iter *Iterator) readFieldHash() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func calcHash(str string, caseSensitive bool) 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) {
|
||||||
if caseSensitive {
|
hash ^= int64(b)
|
||||||
hash ^= int64(b)
|
|
||||||
} else {
|
|
||||||
hash ^= int64(unicode.ToLower(b))
|
|
||||||
}
|
|
||||||
hash *= 0x1000193
|
hash *= 0x1000193
|
||||||
}
|
}
|
||||||
return int64(hash)
|
return int64(hash)
|
||||||
@ -113,6 +112,9 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
var field string
|
var field string
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
@ -122,6 +124,7 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
}
|
}
|
||||||
if !callback(iter, field) {
|
if !callback(iter, field) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
@ -132,20 +135,23 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|||||||
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
|
||||||
}
|
}
|
||||||
if !callback(iter, field) {
|
if !callback(iter, field) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
}
|
}
|
||||||
if c != '}' {
|
if c != '}' {
|
||||||
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
iter.ReportError("ReadObjectCB", `object not ended with }`)
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
if c == '}' {
|
if c == '}' {
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c}))
|
iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c}))
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c == 'n' {
|
if c == 'n' {
|
||||||
@ -160,15 +166,20 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|||||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
field := iter.ReadString()
|
field := iter.ReadString()
|
||||||
if iter.nextToken() != ':' {
|
if iter.nextToken() != ':' {
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !callback(iter, field) {
|
if !callback(iter, field) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
@ -176,23 +187,27 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
|||||||
field = iter.ReadString()
|
field = iter.ReadString()
|
||||||
if iter.nextToken() != ':' {
|
if iter.nextToken() != ':' {
|
||||||
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !callback(iter, field) {
|
if !callback(iter, field) {
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
}
|
}
|
||||||
if c != '}' {
|
if c != '}' {
|
||||||
iter.ReportError("ReadMapCB", `object not ended with }`)
|
iter.ReportError("ReadMapCB", `object not ended with }`)
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
if c == '}' {
|
if c == '}' {
|
||||||
return true
|
return iter.decrementDepth()
|
||||||
}
|
}
|
||||||
iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c}))
|
iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c}))
|
||||||
|
iter.decrementDepth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c == 'n' {
|
if c == 'n' {
|
||||||
|
25
iter_skip.go
25
iter_skip.go
@ -37,17 +37,24 @@ func (iter *Iterator) SkipAndReturnBytes() []byte {
|
|||||||
return iter.stopCapture()
|
return iter.stopCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
type captureBuffer struct {
|
// SkipAndAppendBytes skips next JSON element and appends its content to
|
||||||
startedAt int
|
// buffer, returning the result.
|
||||||
captured []byte
|
func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte {
|
||||||
|
iter.startCaptureTo(buf, iter.head)
|
||||||
|
iter.Skip()
|
||||||
|
return iter.stopCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) startCapture(captureStartedAt int) {
|
func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) {
|
||||||
if iter.captured != nil {
|
if iter.captured != nil {
|
||||||
panic("already in capture mode")
|
panic("already in capture mode")
|
||||||
}
|
}
|
||||||
iter.captureStartedAt = captureStartedAt
|
iter.captureStartedAt = captureStartedAt
|
||||||
iter.captured = make([]byte, 0, 32)
|
iter.captured = buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) startCapture(captureStartedAt int) {
|
||||||
|
iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) stopCapture() []byte {
|
func (iter *Iterator) stopCapture() []byte {
|
||||||
@ -58,13 +65,7 @@ func (iter *Iterator) stopCapture() []byte {
|
|||||||
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
||||||
iter.captureStartedAt = -1
|
iter.captureStartedAt = -1
|
||||||
iter.captured = nil
|
iter.captured = nil
|
||||||
if len(captured) == 0 {
|
return append(captured, remaining...)
|
||||||
copied := make([]byte, len(remaining))
|
|
||||||
copy(copied, remaining)
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
captured = append(captured, remaining...)
|
|
||||||
return captured
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip skips a json object and positions to relatively the next json object
|
// Skip skips a json object and positions to relatively the next json object
|
||||||
|
@ -22,6 +22,9 @@ func (iter *Iterator) skipNumber() {
|
|||||||
|
|
||||||
func (iter *Iterator) skipArray() {
|
func (iter *Iterator) skipArray() {
|
||||||
level := 1
|
level := 1
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
switch iter.buf[i] {
|
switch iter.buf[i] {
|
||||||
@ -31,8 +34,14 @@ func (iter *Iterator) skipArray() {
|
|||||||
i = iter.head - 1 // it will be i++ soon
|
i = iter.head - 1 // it will be i++ soon
|
||||||
case '[': // If open symbol, increase level
|
case '[': // If open symbol, increase level
|
||||||
level++
|
level++
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
case ']': // If close symbol, increase level
|
case ']': // If close symbol, increase level
|
||||||
level--
|
level--
|
||||||
|
if !iter.decrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
// If we have returned to the original level, we're done
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
@ -50,6 +59,10 @@ func (iter *Iterator) skipArray() {
|
|||||||
|
|
||||||
func (iter *Iterator) skipObject() {
|
func (iter *Iterator) skipObject() {
|
||||||
level := 1
|
level := 1
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
switch iter.buf[i] {
|
switch iter.buf[i] {
|
||||||
@ -59,8 +72,14 @@ func (iter *Iterator) skipObject() {
|
|||||||
i = iter.head - 1 // it will be i++ soon
|
i = iter.head - 1 // it will be i++ soon
|
||||||
case '{': // If open symbol, increase level
|
case '{': // If open symbol, increase level
|
||||||
level++
|
level++
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
case '}': // If close symbol, increase level
|
case '}': // If close symbol, increase level
|
||||||
level--
|
level--
|
||||||
|
if !iter.decrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
// If we have returned to the original level, we're done
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
|
@ -2,12 +2,22 @@
|
|||||||
|
|
||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
func (iter *Iterator) skipNumber() {
|
func (iter *Iterator) skipNumber() {
|
||||||
if !iter.trySkipNumber() {
|
if !iter.trySkipNumber() {
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
iter.ReadFloat32()
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
iter.ReadFloat64()
|
||||||
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
|
iter.Error = nil
|
||||||
|
iter.ReadBigFloat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +158,27 @@ func Test_encode_byte_array(t *testing.T) {
|
|||||||
should.Equal(`"AQID"`, string(bytes))
|
should.Equal(`"AQID"`, string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_encode_empty_byte_array(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
bytes, err := json.Marshal([]byte{})
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`""`, string(bytes))
|
||||||
|
bytes, err = jsoniter.Marshal([]byte{})
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`""`, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encode_nil_byte_array(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
var nilSlice []byte
|
||||||
|
bytes, err := json.Marshal(nilSlice)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`null`, string(bytes))
|
||||||
|
bytes, err = jsoniter.Marshal(nilSlice)
|
||||||
|
should.Nil(err)
|
||||||
|
should.Equal(`null`, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
func Test_decode_byte_array_from_base64(t *testing.T) {
|
func Test_decode_byte_array_from_base64(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
data := []byte{}
|
data := []byte{}
|
||||||
|
@ -2,6 +2,7 @@ package misc_tests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
@ -77,6 +78,26 @@ func Test_read_number(t *testing.T) {
|
|||||||
should.Equal(`92233720368547758079223372036854775807`, string(val))
|
should.Equal(`92233720368547758079223372036854775807`, string(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_encode_inf(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
_, err := json.Marshal(math.Inf(1))
|
||||||
|
should.Error(err)
|
||||||
|
_, err = jsoniter.Marshal(float32(math.Inf(1)))
|
||||||
|
should.Error(err)
|
||||||
|
_, err = jsoniter.Marshal(math.Inf(-1))
|
||||||
|
should.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_encode_nan(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
_, err := json.Marshal(math.NaN())
|
||||||
|
should.Error(err)
|
||||||
|
_, err = jsoniter.Marshal(float32(math.NaN()))
|
||||||
|
should.Error(err)
|
||||||
|
_, err = jsoniter.Marshal(math.NaN())
|
||||||
|
should.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
func Benchmark_jsoniter_float(b *testing.B) {
|
func Benchmark_jsoniter_float(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
input := []byte(`1.1123,`)
|
input := []byte(`1.1123,`)
|
||||||
|
@ -114,7 +114,7 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) {
|
|||||||
|
|
||||||
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.NoError(err)
|
should.NoError(err)
|
||||||
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
|
should.Equal(42, wrapper.Payload.(*Payload).Value)
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.NoError(err)
|
should.NoError(err)
|
||||||
@ -128,7 +128,7 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) {
|
|||||||
|
|
||||||
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.Equal(nil, err)
|
||||||
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
|
should.Equal(42, wrapper.Payload.(*Payload).Value)
|
||||||
|
|
||||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.Equal(nil, err)
|
||||||
|
@ -42,3 +42,11 @@ func Test_map_eface_of_eface(t *testing.T) {
|
|||||||
should.NoError(err)
|
should.NoError(err)
|
||||||
should.Equal(`{"1":2,"3":"4"}`, output)
|
should.Equal(`{"1":2,"3":"4"}`, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_encode_nil_map(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
var nilMap map[string]string
|
||||||
|
output, err := jsoniter.MarshalToString(nilMap)
|
||||||
|
should.NoError(err)
|
||||||
|
should.Equal(`null`, output)
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +16,243 @@ type Level2 struct {
|
|||||||
World string
|
World string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_deep_nested(t *testing.T) {
|
||||||
|
type unstructured interface{}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
expectError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "array under maxDepth",
|
||||||
|
data: []byte(`{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`),
|
||||||
|
expectError: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "array over maxDepth",
|
||||||
|
data: []byte(`{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`),
|
||||||
|
expectError: "max depth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object under maxDepth",
|
||||||
|
data: []byte(`{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`),
|
||||||
|
expectError: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object over maxDepth",
|
||||||
|
data: []byte(`{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`),
|
||||||
|
expectError: "max depth",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
targets := []struct {
|
||||||
|
name string
|
||||||
|
new func() interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "unstructured",
|
||||||
|
new: func() interface{} {
|
||||||
|
var v interface{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed named field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed missing field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 1 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 2 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 3 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 4 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 5 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 6 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 7 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
G interface{} `json:"g"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 8 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
G interface{} `json:"g"`
|
||||||
|
H interface{} `json:"h"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 9 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
G interface{} `json:"g"`
|
||||||
|
H interface{} `json:"h"`
|
||||||
|
I interface{} `json:"i"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 10 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
G interface{} `json:"g"`
|
||||||
|
H interface{} `json:"h"`
|
||||||
|
I interface{} `json:"i"`
|
||||||
|
J interface{} `json:"j"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typed 11 field",
|
||||||
|
new: func() interface{} {
|
||||||
|
v := struct {
|
||||||
|
A interface{} `json:"a"`
|
||||||
|
B interface{} `json:"b"`
|
||||||
|
C interface{} `json:"c"`
|
||||||
|
D interface{} `json:"d"`
|
||||||
|
E interface{} `json:"e"`
|
||||||
|
F interface{} `json:"f"`
|
||||||
|
G interface{} `json:"g"`
|
||||||
|
H interface{} `json:"h"`
|
||||||
|
I interface{} `json:"i"`
|
||||||
|
J interface{} `json:"j"`
|
||||||
|
K interface{} `json:"k"`
|
||||||
|
}{}
|
||||||
|
return &v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
for _, target := range targets {
|
||||||
|
t.Run(target.name, func(t *testing.T) {
|
||||||
|
err := jsoniter.Unmarshal(tc.data, target.new())
|
||||||
|
if len(tc.expectError) == 0 {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, got none")
|
||||||
|
} else if !strings.Contains(err.Error(), tc.expectError) {
|
||||||
|
t.Errorf("expected error containing '%s', got: %v", tc.expectError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_nested(t *testing.T) {
|
func Test_nested(t *testing.T) {
|
||||||
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
|
||||||
l1 := Level1{}
|
l1 := Level1{}
|
||||||
|
11
reflect.go
11
reflect.go
@ -60,6 +60,7 @@ func (b *ctx) append(prefix string) *ctx {
|
|||||||
|
|
||||||
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
||||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
func (iter *Iterator) ReadVal(obj interface{}) {
|
||||||
|
depth := iter.depth
|
||||||
cacheKey := reflect2.RTypeOf(obj)
|
cacheKey := reflect2.RTypeOf(obj)
|
||||||
decoder := iter.cfg.getDecoderFromCache(cacheKey)
|
decoder := iter.cfg.getDecoderFromCache(cacheKey)
|
||||||
if decoder == nil {
|
if decoder == nil {
|
||||||
@ -76,6 +77,10 @@ func (iter *Iterator) ReadVal(obj interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
decoder.Decode(ptr, iter)
|
decoder.Decode(ptr, iter)
|
||||||
|
if iter.depth != depth {
|
||||||
|
iter.ReportError("ReadVal", "unexpected mismatched nesting")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
||||||
@ -120,7 +125,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
|
||||||
@ -222,7 +228,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
|
||||||
}
|
}
|
||||||
@ -328,13 +338,13 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
|
|||||||
for i := 0; i < structType.NumField(); i++ {
|
for i := 0; i < structType.NumField(); i++ {
|
||||||
field := structType.Field(i)
|
field := structType.Field(i)
|
||||||
tag, hastag := field.Tag().Lookup(ctx.getTagKey())
|
tag, hastag := field.Tag().Lookup(ctx.getTagKey())
|
||||||
if ctx.onlyTaggedField && !hastag {
|
if ctx.onlyTaggedField && !hastag && !field.Anonymous() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tagParts := strings.Split(tag, ",")
|
|
||||||
if tag == "-" {
|
if tag == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
tagParts := strings.Split(tag, ",")
|
||||||
if field.Anonymous() && (tag == "" || tagParts[0] == "") {
|
if field.Anonymous() && (tag == "" || tagParts[0] == "") {
|
||||||
if field.Type().Kind() == reflect.Struct {
|
if field.Type().Kind() == reflect.Struct {
|
||||||
structDescriptor := describeStruct(ctx, field.Type())
|
structDescriptor := describeStruct(ctx, field.Type())
|
||||||
@ -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
|
||||||
@ -60,14 +64,26 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|||||||
return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
|
return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
|
||||||
default:
|
default:
|
||||||
ptrType := reflect2.PtrTo(typ)
|
ptrType := reflect2.PtrTo(typ)
|
||||||
if ptrType.Implements(textMarshalerType) {
|
if ptrType.Implements(unmarshalerType) {
|
||||||
|
return &referenceDecoder{
|
||||||
|
&unmarshalerDecoder{
|
||||||
|
valType: ptrType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ.Implements(unmarshalerType) {
|
||||||
|
return &unmarshalerDecoder{
|
||||||
|
valType: typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ptrType.Implements(textUnmarshalerType) {
|
||||||
return &referenceDecoder{
|
return &referenceDecoder{
|
||||||
&textUnmarshalerDecoder{
|
&textUnmarshalerDecoder{
|
||||||
valType: ptrType,
|
valType: ptrType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if typ.Implements(textMarshalerType) {
|
if typ.Implements(textUnmarshalerType) {
|
||||||
return &textUnmarshalerDecoder{
|
return &textUnmarshalerDecoder{
|
||||||
valType: typ,
|
valType: typ,
|
||||||
}
|
}
|
||||||
@ -77,7 +93,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
|
||||||
@ -229,6 +249,10 @@ type mapEncoder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
|
if *(*unsafe.Pointer)(ptr) == nil {
|
||||||
|
stream.WriteNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
stream.WriteObjectStart()
|
stream.WriteObjectStart()
|
||||||
iter := encoder.mapType.UnsafeIterate(ptr)
|
iter := encoder.mapType.UnsafeIterate(ptr)
|
||||||
for i := 0; iter.HasNext(); i++ {
|
for i := 0; iter.HasNext(); i++ {
|
||||||
|
@ -3,8 +3,9 @@ package jsoniter
|
|||||||
import (
|
import (
|
||||||
"encoding"
|
"encoding"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/modern-go/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem()
|
var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem()
|
||||||
@ -98,6 +99,12 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
stream.Error = err
|
stream.Error = err
|
||||||
} else {
|
} else {
|
||||||
|
// html escape was already done by jsoniter
|
||||||
|
// but the extra '\n' should be trimed
|
||||||
|
l := len(bytes)
|
||||||
|
if l > 0 && bytes[l-1] == '\n' {
|
||||||
|
bytes = bytes[:l-1]
|
||||||
|
}
|
||||||
stream.Write(bytes)
|
stream.Write(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,17 +432,19 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
src := *((*[]byte)(ptr))
|
if codec.sliceType.UnsafeIsNil(ptr) {
|
||||||
if len(src) == 0 {
|
|
||||||
stream.WriteNil()
|
stream.WriteNil()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
src := *((*[]byte)(ptr))
|
||||||
encoding := base64.StdEncoding
|
encoding := base64.StdEncoding
|
||||||
stream.writeByte('"')
|
stream.writeByte('"')
|
||||||
size := encoding.EncodedLen(len(src))
|
if len(src) != 0 {
|
||||||
buf := make([]byte, size)
|
size := encoding.EncodedLen(len(src))
|
||||||
encoding.Encode(buf, src)
|
buf := make([]byte, size)
|
||||||
stream.buf = append(stream.buf, buf...)
|
encoding.Encode(buf, src)
|
||||||
|
stream.buf = append(stream.buf, buf...)
|
||||||
|
}
|
||||||
stream.writeByte('"')
|
stream.writeByte('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,6 +500,9 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
var c byte
|
var c byte
|
||||||
for c = ','; c == ','; c = iter.nextToken() {
|
for c = ','; c == ','; c = iter.nextToken() {
|
||||||
decoder.decodeOneField(ptr, iter)
|
decoder.decodeOneField(ptr, iter)
|
||||||
@ -510,6 +513,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
if c != '}' {
|
if c != '}' {
|
||||||
iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c}))
|
iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c}))
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
|
func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
@ -530,8 +534,8 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fieldDecoder == nil {
|
if fieldDecoder == nil {
|
||||||
msg := "found unknown field: " + field
|
|
||||||
if decoder.disallowUnknownFields {
|
if decoder.disallowUnknownFields {
|
||||||
|
msg := "found unknown field: " + field
|
||||||
iter.ReportError("ReadObject", msg)
|
iter.ReportError("ReadObject", msg)
|
||||||
}
|
}
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
@ -571,6 +575,9 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if iter.readFieldHash() == decoder.fieldHash {
|
if iter.readFieldHash() == decoder.fieldHash {
|
||||||
decoder.fieldDecoder.Decode(ptr, iter)
|
decoder.fieldDecoder.Decode(ptr, iter)
|
||||||
@ -584,6 +591,7 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type twoFieldsStructDecoder struct {
|
type twoFieldsStructDecoder struct {
|
||||||
@ -598,6 +606,9 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -614,6 +625,7 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type threeFieldsStructDecoder struct {
|
type threeFieldsStructDecoder struct {
|
||||||
@ -630,6 +642,9 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -648,6 +663,7 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type fourFieldsStructDecoder struct {
|
type fourFieldsStructDecoder struct {
|
||||||
@ -666,6 +682,9 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -686,6 +705,7 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type fiveFieldsStructDecoder struct {
|
type fiveFieldsStructDecoder struct {
|
||||||
@ -706,6 +726,9 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -728,6 +751,7 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type sixFieldsStructDecoder struct {
|
type sixFieldsStructDecoder struct {
|
||||||
@ -750,6 +774,9 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -774,6 +801,7 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type sevenFieldsStructDecoder struct {
|
type sevenFieldsStructDecoder struct {
|
||||||
@ -798,6 +826,9 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -824,6 +855,7 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type eightFieldsStructDecoder struct {
|
type eightFieldsStructDecoder struct {
|
||||||
@ -850,6 +882,9 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -878,6 +913,7 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type nineFieldsStructDecoder struct {
|
type nineFieldsStructDecoder struct {
|
||||||
@ -906,6 +942,9 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -936,6 +975,7 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type tenFieldsStructDecoder struct {
|
type tenFieldsStructDecoder struct {
|
||||||
@ -966,6 +1006,9 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
if !iter.readObjectStart() {
|
if !iter.readObjectStart() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !iter.incrementDepth() {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
switch iter.readFieldHash() {
|
switch iter.readFieldHash() {
|
||||||
case decoder.fieldHash1:
|
case decoder.fieldHash1:
|
||||||
@ -998,6 +1041,7 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
|
|||||||
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())
|
||||||
}
|
}
|
||||||
|
iter.decrementDepth()
|
||||||
}
|
}
|
||||||
|
|
||||||
type structFieldDecoder struct {
|
type structFieldDecoder struct {
|
||||||
|
@ -105,6 +105,15 @@ func Test_skip_and_return_bytes_with_reader(t *testing.T) {
|
|||||||
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
|
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(skipped))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_append_skip_and_return_bytes_with_reader(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
iter := jsoniter.Parse(jsoniter.ConfigDefault, bytes.NewBufferString(`[ {"a" : [{"stream": "c"}], "d": 102 }, "stream"]`), 4)
|
||||||
|
iter.ReadArray()
|
||||||
|
buf := make([]byte, 0, 1024)
|
||||||
|
buf = iter.SkipAndAppendBytes(buf)
|
||||||
|
should.Equal(`{"a" : [{"stream": "c"}], "d": 102 }`, string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
func Test_skip_empty(t *testing.T) {
|
func Test_skip_empty(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
should.NotNil(jsoniter.Get([]byte("")).LastError())
|
should.NotNil(jsoniter.Get([]byte("")).LastError())
|
||||||
|
@ -13,7 +13,7 @@ func init() {
|
|||||||
`{"hello":{}}`, // valid
|
`{"hello":{}}`, // valid
|
||||||
`{"hello":{}}}`, // invalid
|
`{"hello":{}}}`, // invalid
|
||||||
`{"hello": { "hello": 1}}`, // valid
|
`{"hello": { "hello": 1}}`, // valid
|
||||||
`{abc}`, // invalid
|
`{abc}`, // invalid
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -13,6 +14,10 @@ func init() {
|
|||||||
|
|
||||||
// WriteFloat32 write float32 to stream
|
// WriteFloat32 write float32 to stream
|
||||||
func (stream *Stream) WriteFloat32(val float32) {
|
func (stream *Stream) WriteFloat32(val float32) {
|
||||||
|
if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) {
|
||||||
|
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
||||||
|
return
|
||||||
|
}
|
||||||
abs := math.Abs(float64(val))
|
abs := math.Abs(float64(val))
|
||||||
fmt := byte('f')
|
fmt := byte('f')
|
||||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||||
@ -26,6 +31,10 @@ func (stream *Stream) WriteFloat32(val float32) {
|
|||||||
|
|
||||||
// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster
|
// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster
|
||||||
func (stream *Stream) WriteFloat32Lossy(val float32) {
|
func (stream *Stream) WriteFloat32Lossy(val float32) {
|
||||||
|
if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) {
|
||||||
|
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
||||||
|
return
|
||||||
|
}
|
||||||
if val < 0 {
|
if val < 0 {
|
||||||
stream.writeByte('-')
|
stream.writeByte('-')
|
||||||
val = -val
|
val = -val
|
||||||
@ -54,6 +63,10 @@ func (stream *Stream) WriteFloat32Lossy(val float32) {
|
|||||||
|
|
||||||
// WriteFloat64 write float64 to stream
|
// WriteFloat64 write float64 to stream
|
||||||
func (stream *Stream) WriteFloat64(val float64) {
|
func (stream *Stream) WriteFloat64(val float64) {
|
||||||
|
if math.IsInf(val, 0) || math.IsNaN(val) {
|
||||||
|
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
||||||
|
return
|
||||||
|
}
|
||||||
abs := math.Abs(val)
|
abs := math.Abs(val)
|
||||||
fmt := byte('f')
|
fmt := byte('f')
|
||||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||||
@ -67,6 +80,10 @@ func (stream *Stream) WriteFloat64(val float64) {
|
|||||||
|
|
||||||
// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster
|
// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster
|
||||||
func (stream *Stream) WriteFloat64Lossy(val float64) {
|
func (stream *Stream) WriteFloat64Lossy(val float64) {
|
||||||
|
if math.IsInf(val, 0) || math.IsNaN(val) {
|
||||||
|
stream.Error = fmt.Errorf("unsupported value: %f", val)
|
||||||
|
return
|
||||||
|
}
|
||||||
if val < 0 {
|
if val < 0 {
|
||||||
stream.writeByte('-')
|
stream.writeByte('-')
|
||||||
val = -val
|
val = -val
|
||||||
|
@ -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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,3 +224,13 @@ func Test_EmptyInput(t *testing.T) {
|
|||||||
t.Errorf("Expected error")
|
t.Errorf("Expected error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
A jsoniter.Any
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_nil_any(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
|
data, _ := jsoniter.Marshal(&Foo{})
|
||||||
|
should.Equal(`{"A":null}`, string(data))
|
||||||
|
}
|
||||||
|
@ -2,7 +2,9 @@ package test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -27,6 +29,8 @@ func init() {
|
|||||||
nilMap,
|
nilMap,
|
||||||
&nilMap,
|
&nilMap,
|
||||||
map[string]*json.RawMessage{"hello": pRawMessage(json.RawMessage("[]"))},
|
map[string]*json.RawMessage{"hello": pRawMessage(json.RawMessage("[]"))},
|
||||||
|
map[Date]bool{{}: true},
|
||||||
|
map[Date2]bool{{}: true},
|
||||||
)
|
)
|
||||||
unmarshalCases = append(unmarshalCases, unmarshalCase{
|
unmarshalCases = append(unmarshalCases, unmarshalCase{
|
||||||
ptr: (*map[string]string)(nil),
|
ptr: (*map[string]string)(nil),
|
||||||
@ -37,6 +41,20 @@ func init() {
|
|||||||
}, unmarshalCase{
|
}, unmarshalCase{
|
||||||
ptr: (*map[string]*json.RawMessage)(nil),
|
ptr: (*map[string]*json.RawMessage)(nil),
|
||||||
input: "{\"test\":[{\"key\":\"value\"}]}",
|
input: "{\"test\":[{\"key\":\"value\"}]}",
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*map[Date]bool)(nil),
|
||||||
|
input: `{
|
||||||
|
"2018-12-12": true,
|
||||||
|
"2018-12-13": true,
|
||||||
|
"2018-12-14": true
|
||||||
|
}`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*map[Date2]bool)(nil),
|
||||||
|
input: `{
|
||||||
|
"2018-12-12": true,
|
||||||
|
"2018-12-13": true,
|
||||||
|
"2018-12-14": true
|
||||||
|
}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,3 +67,51 @@ type MyString string
|
|||||||
func (ms MyString) Hello() string {
|
func (ms MyString) Hello() string {
|
||||||
return string(ms)
|
return string(ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Date struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Date) UnmarshalJSON(b []byte) error {
|
||||||
|
dateStr := string(b) // something like `"2017-08-20"`
|
||||||
|
|
||||||
|
if dateStr == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.Parse(`"2006-01-02"`, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cant parse date: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Time = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Date) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(d.Time.Format("2006-01-02")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Date2 struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Date2) UnmarshalJSON(b []byte) error {
|
||||||
|
dateStr := string(b) // something like `"2017-08-20"`
|
||||||
|
|
||||||
|
if dateStr == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.Parse(`"2006-01-02"`, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cant parse date: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Time = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Date2) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(d.Time.Format("2006-01-02")), nil
|
||||||
|
}
|
||||||
|
@ -1,13 +1,24 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
marshalCases = append(marshalCases,
|
marshalCases = append(marshalCases,
|
||||||
json.RawMessage("{}"),
|
json.RawMessage("{}"),
|
||||||
|
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),
|
||||||
input: `[1,2,3]`,
|
input: `[1,2,3]`,
|
||||||
|
}, unmarshalCase{
|
||||||
|
ptr: (*json.RawMessage)(nil),
|
||||||
|
input: `1.122e+250`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user