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

61 Commits

Author SHA1 Message Date
0ff49de124 update README 2019-03-06 22:29:09 +08:00
5bc9320502 Merge pull request #316 from proemergotech/master
fix #308 do NOT skip embedded structs without tag when OnlyTaggedFiel…
2019-02-08 15:56:42 +08:00
f64ce68b6e Merge pull request #338 from dvrkps/master
Clean go vet error and ineffassign warnings.
2019-01-14 23:53:30 +08:00
2d42ff74dd Merge pull request #337 from denverdino/fix-encode-with-MarshalJSON
Fix the incompatible encoding #336
2019-01-14 23:52:16 +08:00
3a023a5fbc clean readPositiveFloat64 2019-01-10 18:00:15 +01:00
16aef10b2b clean readPositiveFloat32 2019-01-10 17:55:28 +01:00
ae4c002f78 rename ExampleMyKey 2019-01-10 17:51:25 +01:00
e4aa2ec063 Fix the incompatible encoding 2019-01-03 18:19:22 +08:00
d05f387f50 fix #317, try parse as BigFloat if overflow 2018-11-12 14:45:56 +08:00
a9403d25cd fix #308 do NOT skip embedded structs without tag when OnlyTaggedField is set to true 2018-10-29 12:00:51 +01:00
05d041de10 fix #313 support json marshaller type as map key 2018-10-24 23:28:41 +08:00
5916df66b3 fix #311 handle nil any 2018-10-24 21:05:37 +08:00
2433035e51 Merge pull request #304 from Quasilyte/quasilyte/emptyFallthrough
use multi-value case clause instead of fallthrough
2018-09-14 09:48:43 +08:00
6dfc0bf2dd Merge pull request #305 from Quasilyte/quasilyte/assignOp
simplify `x = x <op> y` to `x <op>= y`
2018-09-14 09:48:25 +08:00
b9be8dd373 Merge pull request #306 from Quasilyte/quasilyte/underef
remove redundant dereferencing expressions
2018-09-14 09:47:54 +08:00
b8d78b6aaf Merge pull request #307 from Quasilyte/quasilyte/commentedOutCode
any_tests: remove commented-out code
2018-09-14 09:47:25 +08:00
7109b5e7dd any_tests: remove commented-out code
Found using https://go-critic.github.io/overview#commentedOutCode-ref
2018-09-13 21:57:53 +03:00
4cc76529e8 remove redundant dereferencing expressions
Found using https://go-critic.github.io/overview#underef-ref
2018-09-13 21:51:59 +03:00
c5ddac9dc3 simplify x = x <op> y to x <op>= y
Found using https://go-critic.github.io/overview#assignOp-ref
2018-09-13 21:48:13 +03:00
f76d712086 use multi-value case clause instead of fallthrough
Found using https://go-critic.github.io/overview#emptyFallthrough-ref
2018-09-13 21:43:37 +03:00
1624edc445 fix #295 decoder more was not compatible with standard library 2018-08-06 14:07:27 +08:00
5d789e5e02 fix #291 omit empty was not handled properly for json raw message 2018-08-06 13:58:33 +08:00
0260c89b54 fix #286 calcHash should use byte not rune to calc hash 2018-08-06 13:23:06 +08:00
10a568c511 fix #293 copy extensions 2018-07-22 11:51:51 +08:00
ab8a2e0c74 fix #276 allow rename when set naming strategy 2018-07-01 15:16:28 +08:00
2fbdfbb595 merge 2018-07-01 13:06:34 +08:00
720ab8dc7f add tests for #283 2018-07-01 13:05:25 +08:00
f2b4162afb Merge pull request #285 from nikhita/fix-case-sensitivity
Fix case sensitivity
2018-06-12 13:28:35 -07:00
3830516ed0 Fix case sensitivity for nested fields 2018-06-12 11:27:24 +05:30
7cceb6c2e3 Merge pull request #282 from caesarxuchao/optional-case-sensitivity
Make case sensitivity optional
2018-06-10 17:13:47 +08:00
b92cf78708 Make case sensitivity optional. Fix
https://github.com/kubernetes/kubernetes/issues/64612
2018-06-07 21:01:05 -07:00
8744d7c5c7 \n should not be ignored in base64 decode 2018-05-26 09:43:29 +08:00
37cc313d18 fix #274, unescape before base64 decode 2018-05-26 09:38:52 +08:00
2ddf6d7582 Merge pull request #266 from ceshihao/fix_base64_with_whitespace
fix base64 contains newline case
2018-04-24 08:46:23 +08:00
6a6742f0a2 fix base64 contains newline characters \r or \n 2018-04-23 23:10:55 +08:00
6c702ce12a fix #264 check io.EOF when test decoder.More 2018-04-20 16:10:56 +08:00
f88871b601 fix #263, support empty string as 0 in fuzz mode 2018-04-18 16:34:54 +08:00
f246f80f14 fix #260, support rename for extra.SupportPrivateFields 2018-04-18 16:28:55 +08:00
51dd70305b add more test for #252 2018-04-18 16:22:47 +08:00
a949c42748 fix #261 should load from reader 2018-04-18 16:11:14 +08:00
f89479f5c0 Merge pull request #257 from ash2k/release-writer
Release writer to enable GC
2018-04-08 08:25:46 +08:00
b858ec296c Release writer to enable GC 2018-04-07 21:40:08 +10:00
885a41a0a6 Merge branch 'master' of https://github.com/json-iterator/go 2018-04-03 13:41:12 +08:00
9e9a97040e always benchmark yourself 2018-04-03 13:41:01 +08:00
fb4d53e4cc Merge pull request #255 from bboreham/error-test
Add a test for input errors, and fix one bug that it finds
2018-04-02 13:50:44 +08:00
b53656d459 Check that a struct ends with closing brace 2018-04-01 22:02:44 +00:00
8f27a81d90 Add a test for input errors
Send various malformed JSON strings into the decoder for each type,
and check we get an error each time.
2018-04-01 22:01:21 +00:00
4930b053b8 explit test case sensitive for #252 2018-03-24 22:38:32 +08:00
06e0f9391e fix #250 case insensitive field match 2018-03-20 21:43:30 +08:00
ca39e5af3e suport encode map[interface{}]interface{} 2018-03-15 21:28:16 +08:00
39acec93e0 expose DecoderOf and EncoderOf 2018-03-14 23:18:20 +08:00
25fa392355 fix #245, always reuse existing value even UseNumber 2018-03-01 19:23:20 +08:00
d51e841de0 fix build on 1.8 2018-02-28 20:29:23 +08:00
3353055b2a use concurrent.Map for 1.8 support 2018-02-28 17:11:57 +08:00
455b3f8bb8 move reflect2 from plz to modern-go 2018-02-28 17:09:30 +08:00
2a93f9003e fix #244 use BinaryAsStringExtension to make []byte pretty, while the output is valid json, but it can not be decoded by other json codec, as \x01 is decoded as \x01 by them, which is not original input 2018-02-27 12:40:48 +08:00
9472474ffd test []byte behavior 2018-02-27 12:04:11 +08:00
ad83167dc6 fix #243 fuzzy decoder should take null as valid input 2018-02-26 23:22:22 +08:00
fff342fd04 gofmt 2018-02-24 22:04:41 +08:00
8d6662b81b fix #242 add CreateMapKeyEncoder and CreateMapKeyDecoder to extension spi 2018-02-24 22:04:11 +08:00
a377e2656b add map key example 2018-02-23 18:20:14 +08:00
93 changed files with 1481 additions and 461 deletions

40
Gopkg.lock generated
View File

@ -2,44 +2,20 @@
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/google/gofuzz"
name = "github.com/modern-go/concurrent"
packages = ["."]
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a"
version = "1.0.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
name = "github.com/v2pro/plz"
packages = [
"concurrent",
"reflect2"
]
revision = "10fc95fad3224a032229e59f6e7023137d82b526"
version = "0.9.1"
name = "github.com/modern-go/reflect2"
packages = ["."]
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "64fe3937a1afce5cb551c06ff7109065c971643e082512243d1071bab428ff14"
inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -19,19 +19,8 @@
# name = "github.com/x/y"
# version = "2.4.0"
ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"]
[[constraint]]
name = "github.com/davecgh/go-spew"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/google/gofuzz"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.1.4"
[[constraint]]
name = "github.com/v2pro/plz"
version = "0.9.1"
name = "github.com/modern-go/reflect2"
version = "1.0.1"

View File

@ -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)
```
Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com
```
# Benchmark
![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png)
@ -31,6 +27,9 @@ Raw Result (easyjson requires static code generation)
| easyjson encode | 883 ns/op | 576 B/op | 3 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
100% compatibility with standard lib

View File

@ -77,7 +77,16 @@ func (adapter *Decoder) Decode(obj interface{}) error {
// More is there more?
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
@ -91,7 +100,7 @@ func (adapter *Decoder) Buffered() io.Reader {
func (adapter *Decoder) UseNumber() {
cfg := adapter.iter.cfg.configBeforeFrozen
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
@ -100,7 +109,7 @@ func (adapter *Decoder) UseNumber() {
func (adapter *Decoder) DisallowUnknownFields() {
cfg := adapter.iter.cfg.configBeforeFrozen
cfg.DisallowUnknownFields = true
adapter.iter.cfg = cfg.frozeWithCacheReuse()
adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions)
}
// NewEncoder same as json.NewEncoder
@ -125,14 +134,14 @@ func (adapter *Encoder) Encode(val interface{}) error {
func (adapter *Encoder) SetIndent(prefix, indent string) {
config := adapter.stream.cfg.configBeforeFrozen
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
func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) {
config := adapter.stream.cfg.configBeforeFrozen
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.

8
any.go
View File

@ -3,11 +3,11 @@ package jsoniter
import (
"errors"
"fmt"
"github.com/modern-go/reflect2"
"io"
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
"strconv"
"unsafe"
)
// Any generic object representation.
@ -312,6 +312,10 @@ func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
any := *(*Any)(ptr)
if any == nil {
stream.WriteNil()
return
}
any.WriteTo(stream)
}

View File

@ -3,8 +3,8 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_empty_array_as_any(t *testing.T) {

View File

@ -4,8 +4,8 @@ import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var boolConvertMap = map[string]bool{

View File

@ -3,8 +3,8 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var floatConvertMap = map[string]float64{
@ -82,10 +82,8 @@ func Test_read_float_to_any(t *testing.T) {
should := require.New(t)
any := jsoniter.WrapFloat64(12.3)
anyFloat64 := float64(12.3)
//negaAnyFloat64 := float64(-1.1)
any2 := jsoniter.WrapFloat64(-1.1)
should.Equal(float64(12.3), any.ToFloat64())
//should.Equal("12.3", any.ToString())
should.True(any.ToBool())
should.Equal(float32(anyFloat64), any.ToFloat32())
should.Equal(int(anyFloat64), any.ToInt())

View File

@ -4,8 +4,8 @@ import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var intConvertMap = map[string]int{

View File

@ -1,9 +1,9 @@
package any_tests
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
)
func Test_wrap_map(t *testing.T) {

View File

@ -1,9 +1,9 @@
package any_tests
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
)
func Test_read_null_as_any(t *testing.T) {

View File

@ -3,8 +3,8 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_object_as_any(t *testing.T) {
@ -118,6 +118,4 @@ func Test_object_wrapper_any_get_all(t *testing.T) {
should.Contains(any.Keys(), "Field1")
should.Contains(any.Keys(), "Field2")
should.NotContains(any.Keys(), "Field3")
//should.Contains(any.GetObject()["Field1"].GetArray()[0], 1)
}

View File

@ -3,29 +3,29 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
var stringConvertMap = map[string]string{
"null": "",
"321.1": "321.1",
`"1.1"`: "1.1",
`"-123.1"`: "-123.1",
"0.0": "0.0",
"0": "0",
`"0"`: "0",
`"0.0"`: "0.0",
`"00.0"`: "00.0",
"true": "true",
"false": "false",
`"true"`: "true",
`"false"`: "false",
`"true123"`: "true123",
`"+1"`: "+1",
"[]": "[]",
"[1,2]": "[1,2]",
"{}": "{}",
"null": "",
"321.1": "321.1",
`"1.1"`: "1.1",
`"-123.1"`: "-123.1",
"0.0": "0.0",
"0": "0",
`"0"`: "0",
`"0.0"`: "0.0",
`"00.0"`: "00.0",
"true": "true",
"false": "false",
`"true"`: "true",
`"false"`: "false",
`"true123"`: "true123",
`"+1"`: "+1",
"[]": "[]",
"[1,2]": "[1,2]",
"{}": "{}",
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
}

View File

@ -3,8 +3,8 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
// if must be valid is useless, just drop this test

View File

@ -3,8 +3,8 @@ package any_tests
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_wrap_and_valuetype_everything(t *testing.T) {

View File

@ -1,10 +1,11 @@
package test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"encoding/json"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_use_number_for_unmarshal(t *testing.T) {
@ -23,7 +24,6 @@ func Test_customize_float_marshal(t *testing.T) {
should.Equal("1.234568", str)
}
func Test_customize_tag_key(t *testing.T) {
type TestObject struct {
@ -46,3 +46,184 @@ func Test_read_large_number_as_interface(t *testing.T) {
should.Nil(err)
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)
}
}
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)
}

View File

@ -2,11 +2,11 @@ package test
import (
"bytes"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
"io/ioutil"
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"io/ioutil"
"testing"
)
func Test_disallowUnknownFields(t *testing.T) {
@ -18,7 +18,6 @@ func Test_disallowUnknownFields(t *testing.T) {
should.Error(decoder.Decode(&obj))
}
func Test_new_decoder(t *testing.T) {
should := require.New(t)
decoder1 := json.NewDecoder(bytes.NewBufferString(`[1][2]`))
@ -57,3 +56,9 @@ func Test_use_number(t *testing.T) {
should.Nil(decoder2.Decode(&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())
}

View File

@ -8,8 +8,8 @@ import (
"testing"
"unicode/utf8"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_new_encoder(t *testing.T) {

View File

@ -1,11 +1,11 @@
package test
import (
"testing"
"github.com/stretchr/testify/require"
"bytes"
"github.com/json-iterator/go"
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
// Standard Encoder has trailing newline.

View File

@ -2,9 +2,9 @@ package test
import (
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
)
func Test_marshal_indent(t *testing.T) {

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

View File

@ -2,10 +2,10 @@ package test
import (
"encoding/json"
"github.com/json-iterator/go"
"io/ioutil"
"os"
"testing"
"github.com/json-iterator/go"
)
//func Test_large_file(t *testing.T) {

View File

@ -3,9 +3,12 @@ package jsoniter
import (
"encoding/json"
"io"
"unsafe"
"github.com/v2pro/plz/reflect2"
"reflect"
"sync"
"unsafe"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2"
)
// Config customize how the API should behave.
@ -21,6 +24,7 @@ type Config struct {
OnlyTaggedField bool
ValidateJsonRawMessage bool
ObjectFieldMustBeSimpleString bool
CaseSensitive bool
}
// API the public interface of this package.
@ -38,6 +42,8 @@ type API interface {
NewDecoder(reader io.Reader) *Decoder
Valid(data []byte) bool
RegisterExtension(extension Extension)
DecoderOf(typ reflect2.Type) ValDecoder
EncoderOf(typ reflect2.Type) ValEncoder
}
// ConfigDefault the default API
@ -59,6 +65,66 @@ var ConfigFastest = Config{
ObjectFieldMustBeSimpleString: true, // do not unescape object field
}.Froze()
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
decoderCache *concurrent.Map
encoderCache *concurrent.Map
encoderExtension Extension
decoderExtension Extension
extraExtensions []Extension
streamPool *sync.Pool
iteratorPool *sync.Pool
caseSensitive bool
}
func (cfg *frozenConfig) initCache() {
cfg.decoderCache = concurrent.NewMap()
cfg.encoderCache = concurrent.NewMap()
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
cfg.decoderCache.Store(cacheKey, decoder)
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
cfg.encoderCache.Store(cacheKey, encoder)
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
decoder, found := cfg.decoderCache.Load(cacheKey)
if found {
return decoder.(ValDecoder)
}
return nil
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
encoder, found := cfg.encoderCache.Load(cacheKey)
if found {
return encoder.(ValEncoder)
}
return nil
}
var cfgCache = concurrent.NewMap()
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
obj, found := cfgCache.Load(cfg)
if found {
return obj.(*frozenConfig)
}
return nil
}
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
cfgCache.Store(cfg, frozenConfig)
}
// Froze forge API from config
func (cfg Config) Froze() API {
api := &frozenConfig{
@ -67,6 +133,7 @@ func (cfg Config) Froze() API {
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
onlyTaggedField: cfg.OnlyTaggedField,
disallowUnknownFields: cfg.DisallowUnknownFields,
caseSensitive: cfg.CaseSensitive,
}
api.streamPool = &sync.Pool{
New: func() interface{} {
@ -93,22 +160,21 @@ func (cfg Config) Froze() API {
if cfg.ValidateJsonRawMessage {
api.validateJsonRawMessage(encoderExtension)
}
if len(encoderExtension) > 0 {
api.extensions = append(api.extensions, encoderExtension)
}
if len(decoderExtension) > 0 {
api.extensions = append(api.extensions, decoderExtension)
}
api.encoderExtension = encoderExtension
api.decoderExtension = decoderExtension
api.configBeforeFrozen = cfg
return api
}
func (cfg Config) frozeWithCacheReuse() *frozenConfig {
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
api := getFrozenConfigFromCache(cfg)
if api != nil {
return api
}
api = cfg.Froze().(*frozenConfig)
for _, extension := range extraExtensions {
api.RegisterExtension(extension)
}
addFrozenConfigToCache(cfg, api)
return api
}
@ -125,7 +191,7 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
stream.WriteRaw(string(rawMessage))
}
}, func(ptr unsafe.Pointer) bool {
return false
return len(*((*json.RawMessage)(ptr))) == 0
}}
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
@ -133,6 +199,11 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
exitingValue := *((*interface{})(ptr))
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
iter.ReadVal(exitingValue)
return
}
if iter.WhatIsNext() == NumberValue {
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
} else {
@ -149,7 +220,9 @@ func (cfg *frozenConfig) getTagKey() string {
}
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 {
@ -244,7 +317,7 @@ func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]
}
newCfg := cfg.configBeforeFrozen
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 {

View File

@ -1,64 +0,0 @@
//+build go1.9
package jsoniter
import (
"sync"
)
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
decoderCache sync.Map
encoderCache sync.Map
extensions []Extension
streamPool *sync.Pool
iteratorPool *sync.Pool
}
func (cfg *frozenConfig) initCache() {
cfg.decoderCache = sync.Map{}
cfg.encoderCache = sync.Map{}
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
cfg.decoderCache.Store(cacheKey, decoder)
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
cfg.encoderCache.Store(cacheKey, encoder)
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
decoder, found := cfg.decoderCache.Load(cacheKey)
if found {
return decoder.(ValDecoder)
}
return nil
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
encoder, found := cfg.encoderCache.Load(cacheKey)
if found {
return encoder.(ValEncoder)
}
return nil
}
var cfgCache = &sync.Map{}
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
obj, found := cfgCache.Load(cfg)
if found {
return obj.(*frozenConfig)
}
return nil
}
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
cfgCache.Store(cfg, frozenConfig)
}

View File

@ -1,70 +0,0 @@
//+build !go1.9
package jsoniter
import (
"sync"
)
type frozenConfig struct {
configBeforeFrozen Config
sortMapKeys bool
indentionStep int
objectFieldMustBeSimpleString bool
onlyTaggedField bool
disallowUnknownFields bool
cacheLock *sync.RWMutex
decoderCache map[uintptr]ValDecoder
encoderCache map[uintptr]ValEncoder
extensions []Extension
streamPool *sync.Pool
iteratorPool *sync.Pool
}
func (cfg *frozenConfig) initCache() {
cfg.cacheLock = &sync.RWMutex{}
cfg.decoderCache = map[uintptr]ValDecoder{}
cfg.encoderCache = map[uintptr]ValEncoder{}
}
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
cfg.cacheLock.Lock()
cfg.decoderCache[cacheKey] = decoder
cfg.cacheLock.Unlock()
}
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
cfg.cacheLock.Lock()
cfg.encoderCache[cacheKey] = encoder
cfg.cacheLock.Unlock()
}
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
cfg.cacheLock.RLock()
decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
cfg.cacheLock.RUnlock()
return decoder
}
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
cfg.cacheLock.RLock()
encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
cfg.cacheLock.RUnlock()
return encoder
}
var cfgCacheLock = &sync.RWMutex{}
var cfgCache = map[Config]*frozenConfig{}
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
cfgCacheLock.RLock()
frozenConfig := cfgCache[cfg]
cfgCacheLock.RUnlock()
return frozenConfig
}
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
cfgCacheLock.Lock()
cfgCache[cfg] = frozenConfig
cfgCacheLock.Unlock()
}

View File

@ -3,6 +3,7 @@ package jsoniter
import (
"fmt"
"os"
"strings"
)
func ExampleMarshal() {
@ -93,3 +94,28 @@ func ExampleGet() {
// Output:
// Crimson
}
func ExampleMyKey() {
hello := MyKey("hello")
output, _ := Marshal(map[*MyKey]string{&hello: "world"})
fmt.Println(string(output))
obj := map[*MyKey]string{}
Unmarshal(output, &obj)
for k, v := range obj {
fmt.Println(*k, v)
}
// Output:
// {"Hello":"world"}
// Hel world
}
type MyKey string
func (m *MyKey) MarshalText() ([]byte, error) {
return []byte(strings.Replace(string(*m), "h", "H", -1)), nil
}
func (m *MyKey) UnmarshalText(text []byte) error {
*m = MyKey(text[:3])
return nil
}

View File

@ -1,12 +1,13 @@
package test
import (
"testing"
"unsafe"
"time"
"bytes"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strconv"
"testing"
"time"
"unsafe"
)
func Test_customize_type_decoder(t *testing.T) {
@ -61,7 +62,6 @@ func Test_customize_field_decoder(t *testing.T) {
}
}
func Test_recursive_empty_interface_customization(t *testing.T) {
t.Skip()
var obj interface{}
@ -99,3 +99,92 @@ func Test_read_custom_interface(t *testing.T) {
should.Nil(err)
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++
}
}

View File

@ -1,11 +1,13 @@
package test
import (
"unsafe"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/stretchr/testify/require"
"reflect"
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"unsafe"
)
type TestObject1 struct {
@ -46,6 +48,53 @@ func Test_customize_field_by_extension(t *testing.T) {
should.Equal(`{"field-1":100}`, str)
}
func Test_customize_map_key_encoder(t *testing.T) {
should := require.New(t)
cfg := jsoniter.Config{}.Froze()
cfg.RegisterExtension(&testMapKeyExtension{})
m := map[int]int{1: 2}
output, err := cfg.MarshalToString(m)
should.NoError(err)
should.Equal(`{"2":2}`, output)
m = map[int]int{}
should.NoError(cfg.UnmarshalFromString(output, &m))
should.Equal(map[int]int{1: 2}, m)
}
type testMapKeyExtension struct {
jsoniter.DummyExtension
}
func (extension *testMapKeyExtension) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ.Kind() == reflect.Int {
return &funcEncoder{
fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
stream.WriteRaw(`"`)
stream.WriteInt(*(*int)(ptr) + 1)
stream.WriteRaw(`"`)
},
}
}
return nil
}
func (extension *testMapKeyExtension) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ.Kind() == reflect.Int {
return &funcDecoder{
fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
i, err := strconv.Atoi(iter.ReadString())
if err != nil {
iter.ReportError("read map key", err.Error())
return
}
i--
*(*int)(ptr) = i
},
}
}
return nil
}
type funcDecoder struct {
fun jsoniter.DecoderFunc
}

View File

@ -0,0 +1,238 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"unicode/utf8"
"unsafe"
)
// safeSet holds the value true if the ASCII character with the given array
// position can be represented inside a JSON string without any further
// escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), and the backslash character ("\").
var safeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'(': true,
')': true,
'*': true,
'+': true,
',': true,
'-': true,
'.': true,
'/': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
':': true,
';': true,
'<': true,
'=': true,
'>': true,
'?': true,
'@': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'V': true,
'W': true,
'X': true,
'Y': true,
'Z': true,
'[': true,
'\\': false,
']': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'{': true,
'|': true,
'}': true,
'~': true,
'\u007f': true,
}
var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
type BinaryAsStringExtension struct {
jsoniter.DummyExtension
}
func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ == binaryType {
return &binaryAsStringCodec{}
}
return nil
}
func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ == binaryType {
return &binaryAsStringCodec{}
}
return nil
}
type binaryAsStringCodec struct {
}
func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
rawBytes := iter.ReadStringAsSlice()
bytes := make([]byte, 0, len(rawBytes))
for i := 0; i < len(rawBytes); i++ {
b := rawBytes[i]
if b == '\\' {
b2 := rawBytes[i+1]
if b2 != '\\' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
return
}
b3 := rawBytes[i+2]
if b3 != 'x' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
return
}
b4 := rawBytes[i+3]
b5 := rawBytes[i+4]
i += 4
b = readHex(iter, b4, b5)
}
bytes = append(bytes, b)
}
*(*[]byte)(ptr) = bytes
}
func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
return len(*((*[]byte)(ptr))) == 0
}
func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
stream.SetBuffer(newBuffer)
}
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
var ret byte
if b1 >= '0' && b1 <= '9' {
ret = b1 - '0'
} else if b1 >= 'a' && b1 <= 'f' {
ret = b1 - 'a' + 10
} else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
return 0
}
ret *= 16
if b2 >= '0' && b2 <= '9' {
ret = b2 - '0'
} else if b2 >= 'a' && b2 <= 'f' {
ret = b2 - 'a' + 10
} else {
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
return 0
}
return ret
}
var hex = "0123456789abcdef"
func writeBytes(space []byte, s []byte) []byte {
space = append(space, '"')
// write string, the fast path, without utf8 and escape support
var i int
var c byte
for i, c = range s {
if c < utf8.RuneSelf && safeSet[c] {
space = append(space, c)
} else {
break
}
}
if i == len(s)-1 {
space = append(space, '"')
return space
}
return writeBytesSlowPath(space, s[i:])
}
func writeBytesSlowPath(space []byte, s []byte) []byte {
start := 0
// for the remaining parts, we process them char by char
var i int
var b byte
for i, b = range s {
if b >= utf8.RuneSelf {
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
start = i + 1
continue
}
if safeSet[b] {
continue
}
if start < i {
space = append(space, s[start:i]...)
}
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
start = i + 1
}
if start < len(s) {
space = append(space, s[start:]...)
}
return append(space, '"')
}

View File

@ -0,0 +1,32 @@
package extra
import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"testing"
)
func init() {
jsoniter.RegisterExtension(&BinaryAsStringExtension{})
}
func TestBinaryAsStringCodec(t *testing.T) {
t.Run("safe set", func(t *testing.T) {
should := require.New(t)
output, err := jsoniter.Marshal([]byte("hello"))
should.NoError(err)
should.Equal(`"hello"`, string(output))
var val []byte
should.NoError(jsoniter.Unmarshal(output, &val))
should.Equal(`hello`, string(val))
})
t.Run("non safe set", func(t *testing.T) {
should := require.New(t)
output, err := jsoniter.Marshal([]byte{1, 2, 3, 15})
should.NoError(err)
should.Equal(`"\\x01\\x02\\x03\\x0f"`, string(output))
var val []byte
should.NoError(jsoniter.Unmarshal(output, &val))
should.Equal([]byte{1, 2, 3, 15}, val)
})
}

View File

@ -9,7 +9,7 @@ import (
"unsafe"
"github.com/json-iterator/go"
"github.com/v2pro/plz/reflect2"
"github.com/modern-go/reflect2"
)
const maxUint = ^uint(0)
@ -183,6 +183,9 @@ func (decoder *fuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Ite
*((*string)(ptr)) = string(number)
case jsoniter.StringValue:
*((*string)(ptr)) = iter.ReadString()
case jsoniter.NilValue:
iter.Skip()
*((*string)(ptr)) = ""
default:
iter.ReportError("fuzzyStringDecoder", "not number or string")
}
@ -208,9 +211,15 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
} else {
str = "0"
}
case jsoniter.NilValue:
iter.Skip()
str = "0"
default:
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
}
if len(str) == 0 {
str = "0"
}
newIter := iter.Pool().BorrowIterator([]byte(str))
defer iter.Pool().ReturnIterator(newIter)
isFloat := strings.IndexByte(str, '.') != -1
@ -244,6 +253,9 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
} else {
*((*float32)(ptr)) = 0
}
case jsoniter.NilValue:
iter.Skip()
*((*float32)(ptr)) = 0
default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
}
@ -273,7 +285,10 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
} else {
*((*float64)(ptr)) = 0
}
case jsoniter.NilValue:
iter.Skip()
*((*float64)(ptr)) = 0
default:
iter.ReportError("fuzzyFloat32Decoder", "not number or string")
iter.ReportError("fuzzyFloat64Decoder", "not number or string")
}
}

View File

@ -37,6 +37,8 @@ func Test_any_to_int64(t *testing.T) {
should.Equal(int64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`10`, &val))
should.Equal(int64(10), val)
should.Nil(jsoniter.UnmarshalFromString(`""`, &val))
should.Equal(int64(0), val)
// bool part
should.Nil(jsoniter.UnmarshalFromString(`false`, &val))
@ -357,3 +359,35 @@ func Test_bad_case(t *testing.T) {
should := require.New(t)
should.Nil(err)
}
func Test_null_to_string(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message string
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_int(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message int
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_float32(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message float32
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}
func Test_null_to_float64(t *testing.T) {
should := require.New(t)
body := []byte(`null`)
var message float64
err := jsoniter.Unmarshal(body, &message)
should.NoError(err)
}

View File

@ -2,6 +2,7 @@ package extra
import (
"github.com/json-iterator/go"
"strings"
"unicode"
)
@ -17,6 +18,16 @@ type namingStrategyExtension struct {
func (extension *namingStrategyExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
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.FromNames = []string{extension.translate(binding.Field.Name())}
}

View File

@ -21,3 +21,30 @@ func Test_lower_case_with_underscores(t *testing.T) {
should.Nil(err)
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))
}

View File

@ -2,6 +2,7 @@ package extra
import (
"github.com/json-iterator/go"
"strings"
"unicode"
)
@ -18,8 +19,36 @@ func (extension *privateFieldsExtension) UpdateStructDescriptor(structDescriptor
for _, binding := range structDescriptor.Fields {
isPrivate := unicode.IsLower(rune(binding.Field.Name()[0]))
if isPrivate {
binding.FromNames = []string{binding.Field.Name()}
binding.ToNames = []string{binding.Field.Name()}
tag, hastag := binding.Field.Tag().Lookup("json")
if !hastag {
binding.FromNames = []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
}

View File

@ -77,14 +77,12 @@ func (iter *Iterator) ReadFloat32() (ret float32) {
}
func (iter *Iterator) readPositiveFloat32() (ret float32) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat32SlowPath()
}
c = iter.buf[i]
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
@ -107,7 +105,7 @@ func (iter *Iterator) readPositiveFloat32() (ret float32) {
return
}
}
value = uint64(ind)
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
@ -145,9 +143,7 @@ non_decimal_loop:
}
// too many decimal places
return iter.readFloat32SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
case invalidCharForNumber, dotInNumber:
return iter.readFloat32SlowPath()
}
decimalPlaces++
@ -218,14 +214,12 @@ func (iter *Iterator) ReadFloat64() (ret float64) {
}
func (iter *Iterator) readPositiveFloat64() (ret float64) {
value := uint64(0)
c := byte(' ')
i := iter.head
// first char
if i == iter.tail {
return iter.readFloat64SlowPath()
}
c = iter.buf[i]
c := iter.buf[i]
i++
ind := floatDigits[c]
switch ind {
@ -248,7 +242,7 @@ func (iter *Iterator) readPositiveFloat64() (ret float64) {
return
}
}
value = uint64(ind)
value := uint64(ind)
// chars before dot
non_decimal_loop:
for ; i < iter.tail; i++ {
@ -286,9 +280,7 @@ non_decimal_loop:
}
// too many decimal places
return iter.readFloat64SlowPath()
case invalidCharForNumber:
fallthrough
case dotInNumber:
case invalidCharForNumber, dotInNumber:
return iter.readFloat64SlowPath()
}
decimalPlaces++

View File

@ -2,7 +2,7 @@ package jsoniter
import (
"fmt"
"unicode"
"strings"
)
// ReadObject read one field from object.
@ -60,7 +60,7 @@ func (iter *Iterator) readFieldHash() int64 {
if b == '\\' {
iter.head = i
for _, b := range iter.readStringSlowPath() {
if 'A' <= b && b <= 'Z' {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
hash ^= int64(b)
@ -82,7 +82,7 @@ func (iter *Iterator) readFieldHash() int64 {
}
return hash
}
if 'A' <= b && b <= 'Z' {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
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)
for _, b := range str {
hash ^= int64(unicode.ToLower(b))
for _, b := range []byte(str) {
hash ^= int64(b)
hash *= 0x1000193
}
return int64(hash)

View File

@ -2,12 +2,22 @@
package jsoniter
import "fmt"
import (
"fmt"
"io"
)
func (iter *Iterator) skipNumber() {
if !iter.trySkipNumber() {
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()
}
}
}

View File

@ -3,9 +3,10 @@ package misc_tests
import (
"bytes"
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
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)
}
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) {
should := require.New(t)
data := []byte{}

View File

@ -4,8 +4,8 @@ import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_true(t *testing.T) {

View File

@ -4,8 +4,8 @@ import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_big_float(t *testing.T) {

View File

@ -9,8 +9,8 @@ import (
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_uint64_invalid(t *testing.T) {

View File

@ -2,10 +2,10 @@ package misc_tests
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"io"
"testing"
)
func Test_nil_non_empty_interface(t *testing.T) {
@ -114,7 +114,7 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) {
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.NoError(err)
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
should.Equal(42, wrapper.Payload.(*Payload).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
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)
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)
should.Equal(nil, err)

View File

@ -7,8 +7,8 @@ import (
"strconv"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_bad_case(t *testing.T) {

View File

@ -5,9 +5,9 @@ import (
"math/big"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
"github.com/json-iterator/go"
)
func Test_decode_TextMarshaler_key_map(t *testing.T) {
@ -31,3 +31,14 @@ func Test_read_map_with_reader(t *testing.T) {
should.Equal(m2, m1)
should.Equal("1.0.76", m1["note"].(map[string]interface{})["CoreServices"].(map[string]interface{})["version_name"])
}
func Test_map_eface_of_eface(t *testing.T) {
should := require.New(t)
json := jsoniter.ConfigCompatibleWithStandardLibrary
output, err := json.MarshalToString(map[interface{}]interface{}{
"1": 2,
3: "4",
})
should.NoError(err)
should.Equal(`{"1":2,"3":"4"}`, output)
}

View File

@ -2,9 +2,9 @@ package misc_tests
import (
"encoding/json"
"github.com/json-iterator/go"
"reflect"
"testing"
"github.com/json-iterator/go"
)
type Level1 struct {

View File

@ -5,8 +5,8 @@ import (
"io"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_read_null(t *testing.T) {

View File

@ -4,10 +4,10 @@ import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"time"
"github.com/stretchr/testify/require"
"strings"
"time"
)
func Test_empty_object(t *testing.T) {
@ -130,3 +130,20 @@ func Test_reader_and_load_more(t *testing.T) {
obj := TestObject{}
should.Nil(decoder.Decode(&obj))
}
func Test_unmarshal_into_existing_value(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 int
Field2 interface{}
}
var obj TestObject
m := map[string]interface{}{}
obj.Field2 = &m
cfg := jsoniter.Config{UseNumber: true}.Froze()
err := cfg.Unmarshal([]byte(`{"Field1":1,"Field2":{"k":"v"}}`), &obj)
should.NoError(err)
should.Equal(map[string]interface{}{
"k": "v",
}, m)
}

View File

@ -2,10 +2,10 @@ package misc_tests
import (
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
"testing"
"github.com/json-iterator/go"
)
func Test_jsoniter_RawMessage(t *testing.T) {
@ -45,18 +45,18 @@ func Test_marshal_invalid_json_raw_message(t *testing.T) {
func Test_raw_message_memory_not_copied_issue(t *testing.T) {
jsonStream := `{"name":"xxxxx","bundle_id":"com.zonst.majiang","app_platform":"ios","app_category":"100103", "budget_day":1000,"bidding_min":1,"bidding_max":2,"bidding_type":"CPM", "freq":{"open":true,"type":"day","num":100},"speed":1, "targeting":{"vendor":{"open":true,"list":["zonst"]}, "geo_code":{"open":true,"list":["156110100"]},"app_category":{"open":true,"list":["100101"]}, "day_parting":{"open":true,"list":["100409","100410"]},"device_type":{"open":true,"list":["ipad"]}, "os_version":{"open":true,"list":[10]},"carrier":{"open":true,"list":["mobile"]}, "network":{"open":true,"list":["4G"]}},"url":{"tracking_imp_url":"http://www.baidu.com", "tracking_clk_url":"http://www.baidu.com","jump_url":"http://www.baidu.com","deep_link_url":"http://www.baidu.com"}}`
type IteratorObject struct {
Name *string `json:"name"`
BundleId *string `json:"bundle_id"`
AppCategory *string `json:"app_category"`
AppPlatform *string `json:"app_platform"`
BudgetDay *float32 `json:"budget_day"`
BiddingMax *float32 `json:"bidding_max"`
BiddingMin *float32 `json:"bidding_min"`
BiddingType *string `json:"bidding_type"`
Name *string `json:"name"`
BundleId *string `json:"bundle_id"`
AppCategory *string `json:"app_category"`
AppPlatform *string `json:"app_platform"`
BudgetDay *float32 `json:"budget_day"`
BiddingMax *float32 `json:"bidding_max"`
BiddingMin *float32 `json:"bidding_min"`
BiddingType *string `json:"bidding_type"`
Freq *jsoniter.RawMessage `json:"freq"`
Targeting *jsoniter.RawMessage `json:"targeting"`
Url *jsoniter.RawMessage `json:"url"`
Speed *int `json:"speed" db:"speed"`
Speed *int `json:"speed" db:"speed"`
}
obj := &IteratorObject{}

View File

@ -23,6 +23,7 @@ func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
}
func (cfg *frozenConfig) ReturnStream(stream *Stream) {
stream.out = nil
stream.Error = nil
stream.Attachment = nil
cfg.streamPool.Put(stream)

View File

@ -4,7 +4,8 @@ import (
"fmt"
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
"github.com/modern-go/reflect2"
)
// ValDecoder is an internal type registered to cache as needed.
@ -40,6 +41,14 @@ type ctx struct {
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 {
return &ctx{
frozenConfig: b.frozenConfig,
@ -111,7 +120,8 @@ func decoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
for _, extension := range extensions {
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)
}
return decoder
@ -213,7 +223,8 @@ func encoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder {
for _, extension := range extensions {
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)
}
return encoder

View File

@ -2,9 +2,9 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"io"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder {

View File

@ -1,9 +1,9 @@
package jsoniter
import (
"github.com/v2pro/plz/reflect2"
"unsafe"
"github.com/modern-go/reflect2"
"reflect"
"unsafe"
)
type dynamicEncoder struct {

View File

@ -2,12 +2,12 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"reflect"
"sort"
"strings"
"unicode"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
var typeDecoders = map[string]ValDecoder{}
@ -18,8 +18,8 @@ var extensions = []Extension{}
// StructDescriptor describe how should we encode/decode the struct
type StructDescriptor struct {
Type reflect2.Type
Fields []*Binding
Type reflect2.Type
Fields []*Binding
}
// GetField get one field from the descriptor by its name.
@ -47,6 +47,8 @@ type Binding struct {
// Can also rename fields by UpdateStructDescriptor.
type Extension interface {
UpdateStructDescriptor(structDescriptor *StructDescriptor)
CreateMapKeyDecoder(typ reflect2.Type) ValDecoder
CreateMapKeyEncoder(typ reflect2.Type) ValEncoder
CreateDecoder(typ reflect2.Type) ValDecoder
CreateEncoder(typ reflect2.Type) ValEncoder
DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder
@ -61,6 +63,16 @@ type DummyExtension struct {
func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}
// CreateMapKeyDecoder No-op
func (extension *DummyExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
return nil
}
// CreateMapKeyEncoder No-op
func (extension *DummyExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
return nil
}
// CreateDecoder No-op
func (extension *DummyExtension) CreateDecoder(typ reflect2.Type) ValDecoder {
return nil
@ -97,6 +109,16 @@ func (extension EncoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder {
return extension[typ]
}
// CreateMapKeyDecoder No-op
func (extension EncoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
return nil
}
// CreateMapKeyEncoder No-op
func (extension EncoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
return nil
}
// DecorateDecoder No-op
func (extension EncoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder {
return decoder
@ -113,6 +135,16 @@ type DecoderExtension map[reflect2.Type]ValDecoder
func (extension DecoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) {
}
// CreateMapKeyDecoder No-op
func (extension DecoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder {
return nil
}
// CreateMapKeyEncoder No-op
func (extension DecoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder {
return nil
}
// CreateDecoder get decoder from map
func (extension DecoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder {
return extension[typ]
@ -214,7 +246,8 @@ func getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
for _, extension := range extensions {
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)
}
}
@ -227,14 +260,18 @@ func _getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder {
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)
if decoder != nil {
return decoder
}
}
typeName := typ.String()
decoder := typeDecoders[typeName]
decoder = typeDecoders[typeName]
if decoder != nil {
return decoder
}
@ -254,7 +291,8 @@ func getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
for _, extension := range extensions {
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)
}
}
@ -268,14 +306,18 @@ func _getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder {
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)
if encoder != nil {
return encoder
}
}
typeName := typ.String()
encoder := typeEncoders[typeName]
encoder = typeEncoders[typeName]
if encoder != nil {
return encoder
}
@ -296,7 +338,7 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
tag, hastag := field.Tag().Lookup(ctx.getTagKey())
if ctx.onlyTaggedField && !hastag {
if ctx.onlyTaggedField && !hastag && !field.Anonymous() {
continue
}
tagParts := strings.Split(tag, ",")
@ -355,13 +397,15 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor {
}
func createStructDescriptor(ctx *ctx, typ reflect2.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
structDescriptor := &StructDescriptor{
Type: typ,
Fields: bindings,
Type: typ,
Fields: bindings,
}
for _, extension := range extensions {
extension.UpdateStructDescriptor(structDescriptor)
}
for _, extension := range ctx.extensions {
ctx.encoderExtension.UpdateStructDescriptor(structDescriptor)
ctx.decoderExtension.UpdateStructDescriptor(structDescriptor)
for _, extension := range ctx.extraExtensions {
extension.UpdateStructDescriptor(structDescriptor)
}
processTags(structDescriptor, ctx.frozenConfig)

View File

@ -2,9 +2,9 @@ package jsoniter
import (
"encoding/json"
"github.com/modern-go/reflect2"
"strconv"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
type Number string

View File

@ -1,9 +1,9 @@
package jsoniter
import (
"unsafe"
"encoding/json"
"github.com/v2pro/plz/reflect2"
"github.com/modern-go/reflect2"
"unsafe"
)
var jsonRawMessageType = reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()

View File

@ -1,11 +1,12 @@
package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"io"
"reflect"
"sort"
"unsafe"
"github.com/v2pro/plz/reflect2"
"fmt"
)
func decoderOfMap(ctx *ctx, typ reflect2.Type) ValDecoder {
@ -38,6 +39,16 @@ func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder {
}
func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
decoder := ctx.decoderExtension.CreateMapKeyDecoder(typ)
if decoder != nil {
return decoder
}
for _, extension := range ctx.extraExtensions {
decoder := extension.CreateMapKeyDecoder(typ)
if decoder != nil {
return decoder
}
}
switch typ.Kind() {
case reflect.String:
return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String))
@ -53,14 +64,26 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
return &numericMapKeyDecoder{decoderOfType(ctx, typ)}
default:
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{
&textUnmarshalerDecoder{
valType: ptrType,
},
}
}
if typ.Implements(textMarshalerType) {
if typ.Implements(textUnmarshalerType) {
return &textUnmarshalerDecoder{
valType: typ,
}
@ -70,6 +93,16 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder {
}
func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
encoder := ctx.encoderExtension.CreateMapKeyEncoder(typ)
if encoder != nil {
return encoder
}
for _, extension := range ctx.extraExtensions {
encoder := extension.CreateMapKeyEncoder(typ)
if encoder != nil {
return encoder
}
}
switch typ.Kind() {
case reflect.String:
return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String))
@ -95,6 +128,9 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder {
stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
}
}
if typ.Kind() == reflect.Interface {
return &dynamicMapKeyEncoder{ctx, typ}
}
return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)}
}
}
@ -191,6 +227,21 @@ func (encoder *numericMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
type dynamicMapKeyEncoder struct {
ctx *ctx
valType reflect2.Type
}
func (encoder *dynamicMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := encoder.valType.UnsafeIndirect(ptr)
encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).Encode(reflect2.PtrOf(obj), stream)
}
func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool {
obj := encoder.valType.UnsafeIndirect(ptr)
return encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).IsEmpty(reflect2.PtrOf(obj))
}
type mapEncoder struct {
mapType *reflect2.UnsafeMapType
keyEncoder ValEncoder
@ -241,6 +292,9 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
subStream.buf = make([]byte, 0, 64)
key, elem := mapIter.UnsafeNext()
encoder.keyEncoder.Encode(key, subStream)
if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil {
stream.Error = subStream.Error
}
encodedKey := subStream.Buffer()
subIter.ResetBytes(encodedKey)
decodedKey := subIter.ReadString()

View File

@ -1,10 +1,10 @@
package jsoniter
import (
"github.com/v2pro/plz/reflect2"
"unsafe"
"encoding"
"encoding/json"
"github.com/modern-go/reflect2"
"unsafe"
)
var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem()
@ -93,8 +93,7 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteNil()
return
}
marshaler := obj.(json.Marshaler)
bytes, err := marshaler.MarshalJSON()
bytes, err := json.Marshal(obj)
if err != nil {
stream.Error = err
} else {

View File

@ -3,9 +3,10 @@ package jsoniter
import (
"encoding/base64"
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
"strconv"
"unsafe"
"github.com/modern-go/reflect2"
)
const ptrSize = 32 << uintptr(^uintptr(0)>>63)
@ -405,7 +406,7 @@ func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
}
type base64Codec struct {
sliceType *reflect2.UnsafeSliceType
sliceType *reflect2.UnsafeSliceType
sliceDecoder ValDecoder
}
@ -416,16 +417,11 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
}
switch iter.WhatIsNext() {
case StringValue:
encoding := base64.StdEncoding
src := iter.SkipAndReturnBytes()
src = src[1: len(src)-1]
decodedLen := encoding.DecodedLen(len(src))
dst := make([]byte, decodedLen)
len, err := encoding.Decode(dst, src)
src := iter.ReadString()
dst, err := base64.StdEncoding.DecodeString(src)
if err != nil {
iter.ReportError("decode base64", err.Error())
} else {
dst = dst[:len]
codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst))
}
case ArrayValue:

View File

@ -1,9 +1,9 @@
package jsoniter
import (
"github.com/modern-go/reflect2"
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder {

View File

@ -2,9 +2,9 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"io"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder {

View File

@ -5,7 +5,8 @@ import (
"io"
"strings"
"unsafe"
"github.com/v2pro/plz/reflect2"
"github.com/modern-go/reflect2"
)
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 {
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)
}
@ -41,12 +51,13 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
knownHash := map[int64]struct{}{
0: {},
}
switch len(fields) {
case 0:
return &skipObjectDecoder{typ}
case 1:
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -60,7 +71,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder1 *structFieldDecoder
var fieldDecoder2 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -83,7 +94,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder2 *structFieldDecoder
var fieldDecoder3 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -114,7 +125,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder3 *structFieldDecoder
var fieldDecoder4 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -151,7 +162,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder4 *structFieldDecoder
var fieldDecoder5 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -194,7 +205,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder5 *structFieldDecoder
var fieldDecoder6 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -243,7 +254,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder6 *structFieldDecoder
var fieldDecoder7 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -298,7 +309,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder7 *structFieldDecoder
var fieldDecoder8 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -359,7 +370,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder8 *structFieldDecoder
var fieldDecoder9 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -426,7 +437,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
var fieldDecoder9 *structFieldDecoder
var fieldDecoder10 *structFieldDecoder
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -489,13 +500,16 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
if !iter.readObjectStart() {
return
}
decoder.decodeOneField(ptr, iter)
for iter.nextToken() == ',' {
var c byte
for c = ','; c == ','; c = iter.nextToken() {
decoder.decodeOneField(ptr, iter)
}
if iter.Error != nil && iter.Error != io.EOF {
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) {
@ -505,13 +519,13 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It
fieldBytes := iter.ReadStringAsSlice()
field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil {
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
} else {
field = iter.ReadString()
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil {
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
}

View File

@ -2,10 +2,10 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"io"
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder {

View File

@ -5,8 +5,8 @@ import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
)
func Test_skip_number_in_array(t *testing.T) {

View File

@ -6,8 +6,8 @@ import (
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"io"
"testing"
"reflect"
"testing"
)
type testCase struct {

View File

@ -13,7 +13,7 @@ func init() {
`{"hello":{}}`, // valid
`{"hello":{}}}`, // invalid
`{"hello": { "hello": 1}}`, // valid
`{abc}`, // invalid
`{abc}`, // invalid
},
})
}

View File

@ -55,6 +55,11 @@ func (stream *Stream) Buffer() []byte {
return stream.buf
}
// SetBuffer allows to append to the internal buffer directly
func (stream *Stream) SetBuffer(buf []byte) {
stream.buf = buf
}
// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining

View File

@ -17,16 +17,16 @@ func init() {
func writeFirstBuf(space []byte, v uint32) []byte {
start := v >> 24
if start == 0 {
space = append(space, byte(v >> 16), byte(v >> 8))
space = append(space, byte(v>>16), byte(v>>8))
} else if start == 1 {
space = append(space, byte(v >> 8))
space = append(space, byte(v>>8))
}
space = append(space, byte(v))
return space
}
func writeBuf(buf []byte, v uint32) []byte {
return append(buf, byte(v >> 16), byte(v >> 8), byte(v))
return append(buf, byte(v>>16), byte(v>>8), byte(v))
}
// WriteUint8 write uint8 to stream
@ -91,7 +91,7 @@ func (stream *Stream) WriteUint32(val uint32) {
stream.buf = writeFirstBuf(stream.buf, digits[q2])
} else {
r3 := q2 - q3*1000
stream.buf = append(stream.buf, byte(q3 + '0'))
stream.buf = append(stream.buf, byte(q3+'0'))
stream.buf = writeBuf(stream.buf, digits[r3])
}
stream.buf = writeBuf(stream.buf, digits[r2])

View File

@ -1,8 +1,8 @@
package test
import (
"strings"
"encoding"
"strings"
)
func init() {
@ -26,7 +26,6 @@ func (k *stringKeyType) UnmarshalText(text []byte) error {
var _ encoding.TextMarshaler = stringKeyType("")
var _ encoding.TextUnmarshaler = new(stringKeyType)
type structKeyType struct {
X string
}

View File

@ -3,8 +3,8 @@ package test
import (
"bytes"
"encoding/base64"
"strings"
"encoding/json"
"strings"
)
type StringMarshaler string

View File

@ -1,10 +1,10 @@
package test
import (
"strings"
"encoding/base64"
"bytes"
"encoding/base64"
"encoding/json"
"strings"
)
type structMarshaler struct {

View File

@ -91,7 +91,6 @@ func (p *jsonMarshaler) UnmarshalJSON(input []byte) error {
return nil
}
type jsonMarshalerMap map[int]int
func (p *jsonMarshalerMap) MarshalJSON() ([]byte, error) {
@ -117,7 +116,6 @@ func (p *textMarshaler) UnmarshalText(input []byte) error {
type textMarshalerMap map[int]int
func (p *textMarshalerMap) MarshalText() ([]byte, error) {
return []byte(`{}`), nil
}

View File

@ -145,6 +145,9 @@ func init() {
(*struct {
Field bool `json:",omitempty,string"`
})(nil),
(*struct {
Field bool `json:"中文"`
})(nil),
)
}
@ -192,12 +195,12 @@ type StringFieldNameE struct {
}
type StringFieldName struct {
F1 string `json:"F1"`
F2 string `json:"f2"`
F3 string `json:"-"`
F4 string `json:"-,"`
F5 string `json:","`
F6 string `json:""`
F1 string `json:"F1"`
F2 string `json:"f2"`
F3 string `json:"-"`
F4 string `json:"-,"`
F5 string `json:","`
F6 string `json:""`
StringFieldNameE `json:"e"`
}

View File

@ -1,10 +1,10 @@
package test
import (
"strings"
"encoding"
"bytes"
"encoding"
"encoding/base64"
"strings"
)
func init() {

View File

@ -2,9 +2,9 @@ package test
import (
"bytes"
"encoding"
"encoding/base64"
"strings"
"encoding"
)
func init() {

View File

@ -1,15 +1,15 @@
package test
import (
"testing"
"reflect"
"fmt"
"github.com/google/gofuzz"
"strings"
"github.com/json-iterator/go"
"encoding/json"
"bytes"
"encoding/json"
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/google/gofuzz"
"github.com/json-iterator/go"
"reflect"
"strings"
"testing"
)
var testCases []interface{}

View File

@ -8,13 +8,13 @@ func init() {
[2]*float64{},
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*[0]int)(nil),
ptr: (*[0]int)(nil),
input: `[1]`,
}, unmarshalCase{
ptr: (*[1]int)(nil),
ptr: (*[1]int)(nil),
input: `[2]`,
}, unmarshalCase{
ptr: (*[1]int)(nil),
ptr: (*[1]int)(nil),
input: `[]`,
})
}

View File

@ -8,16 +8,16 @@ func init() {
return &val
}
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (**interface{})(nil),
ptr: (**interface{})(nil),
input: `"hello"`,
}, unmarshalCase{
ptr: (**interface{})(nil),
ptr: (**interface{})(nil),
input: `1e1`,
}, unmarshalCase{
ptr: (**interface{})(nil),
ptr: (**interface{})(nil),
input: `1.0e1`,
}, unmarshalCase{
ptr: (*[]interface{})(nil),
ptr: (*[]interface{})(nil),
input: `[1.0e1]`,
}, unmarshalCase{
ptr: (*struct {
@ -66,12 +66,12 @@ func init() {
}{"hello"},
struct {
Field interface{}
}{struct{
}{struct {
field chan int
}{}},
struct {
Field interface{}
}{struct{
}{struct {
Field *int
}{pInt(100)}},
)

36
value_tests/error_test.go Normal file
View 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)
}
})
}
}

View File

@ -1,13 +1,13 @@
package test
import (
"bytes"
"encoding/json"
"fmt"
"testing"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strconv"
"bytes"
"github.com/json-iterator/go"
"encoding/json"
"testing"
)
func Test_read_float(t *testing.T) {
@ -50,7 +50,6 @@ func Test_read_float(t *testing.T) {
}
}
func Test_write_float32(t *testing.T) {
vals := []float32{0, 1, -1, 99, 0xff, 0xfff, 0xffff, 0xfffff, 0xffffff, 0x4ffffff, 0xfffffff,
-0x4ffffff, -0xfffffff, 1.2345, 1.23456, 1.234567, 1.001}

View File

@ -17,7 +17,7 @@ func init() {
pCloser2("hello"),
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*[]io.Closer)(nil),
ptr: (*[]io.Closer)(nil),
input: "[null]",
}, unmarshalCase{
obj: func() interface{} {

View File

@ -1,12 +1,12 @@
package test
import (
"bytes"
"fmt"
"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strconv"
"fmt"
"testing"
"bytes"
"github.com/json-iterator/go"
)
func init() {
@ -158,7 +158,6 @@ func Test_read_int64(t *testing.T) {
}
}
func Test_write_uint8(t *testing.T) {
vals := []uint8{0, 1, 11, 111, 255}
for _, val := range vals {

View File

@ -3,11 +3,11 @@ package test
import (
"bytes"
"encoding/json"
"github.com/json-iterator/go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io"
"testing"
"github.com/json-iterator/go"
)
func Test_missing_object_end(t *testing.T) {
@ -224,3 +224,13 @@ func Test_EmptyInput(t *testing.T) {
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))
}

View File

@ -1,8 +1,10 @@
package test
import (
"math/big"
"encoding/json"
"fmt"
"math/big"
"time"
)
func init() {
@ -26,17 +28,33 @@ func init() {
},
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{
ptr: (*map[string]string)(nil),
ptr: (*map[string]string)(nil),
input: `{"k\"ey": "val"}`,
}, unmarshalCase{
ptr: (*map[string]string)(nil),
ptr: (*map[string]string)(nil),
input: `null`,
}, unmarshalCase{
ptr: (*map[string]*json.RawMessage)(nil),
ptr: (*map[string]*json.RawMessage)(nil),
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 {
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
}

View File

@ -1,8 +1,8 @@
package test
import (
"encoding/json"
"encoding"
"encoding/json"
)
func init() {
@ -17,20 +17,20 @@ func init() {
tmOfStructInt{},
&tm2,
map[tmOfStruct]int{
tmOfStruct{}: 100,
{}: 100,
},
map[*tmOfStruct]int{
&tmOfStruct{}: 100,
{}: 100,
},
map[encoding.TextMarshaler]int{
tm1: 100,
},
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*tmOfMap)(nil),
ptr: (*tmOfMap)(nil),
input: `"{1:2}"`,
}, unmarshalCase{
ptr: (*tmOfMapPtr)(nil),
ptr: (*tmOfMapPtr)(nil),
input: `"{1:2}"`,
})
}
@ -47,7 +47,6 @@ func (q *jmOfStruct) UnmarshalJSON(value []byte) error {
return nil
}
type tmOfStruct struct {
F2 chan []byte
}

View File

@ -1,13 +1,24 @@
package test
import "encoding/json"
import (
"encoding/json"
)
func init() {
marshalCases = append(marshalCases,
json.RawMessage("{}"),
struct {
Env string `json:"env"`
Extra json.RawMessage `json:"extra,omitempty"`
}{
Env: "jfdk",
},
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*json.RawMessage)(nil),
ptr: (*json.RawMessage)(nil),
input: `[1,2,3]`,
}, unmarshalCase{
ptr: (*json.RawMessage)(nil),
input: `1.122e+250`,
})
}

View File

@ -6,6 +6,7 @@ func init() {
[]interface{}{"hello"},
nilSlice,
&nilSlice,
[]byte{1, 2, 3},
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*[]string)(nil),
@ -13,5 +14,14 @@ func init() {
}, unmarshalCase{
ptr: (*[]string)(nil),
input: "[]",
}, unmarshalCase{
ptr: (*[]byte)(nil),
input: "[1,2,3]",
}, unmarshalCase{
ptr: (*[]byte)(nil),
input: `"aGVsbG8="`,
}, unmarshalCase{
ptr: (*[]byte)(nil),
input: `"c3ViamVjdHM\/X2Q9MQ=="`,
})
}

View File

@ -1,9 +1,9 @@
package test
import (
"testing"
"github.com/json-iterator/go"
"encoding/json"
"github.com/json-iterator/go"
"testing"
"unicode/utf8"
)

View File

@ -1,9 +1,9 @@
package test
import (
"time"
"encoding/json"
"bytes"
"encoding/json"
"time"
)
func init() {
@ -63,6 +63,43 @@ func init() {
d *time.Timer
})(nil),
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,
struct {

View File

@ -1,18 +1,18 @@
package test
import (
"testing"
"encoding/json"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"fmt"
"github.com/v2pro/plz/reflect2"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/stretchr/testify/require"
"testing"
)
type unmarshalCase struct {
obj func() interface{}
ptr interface{}
input string
obj func() interface{}
ptr interface{}
input string
selected bool
}
@ -22,7 +22,7 @@ var marshalCases = []interface{}{
nil,
}
type selectedMarshalCase struct {
type selectedMarshalCase struct {
marshalCase interface{}
}