Skip to content

Commit 3830516

Browse files
committed
Fix case sensitivity for nested fields
1 parent 7cceb6c commit 3830516

File tree

2 files changed

+138
-6
lines changed

2 files changed

+138
-6
lines changed

api_tests/config_test.go

+128-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package test
22

33
import (
44
"encoding/json"
5+
"testing"
6+
57
"github.com/json-iterator/go"
68
"github.com/stretchr/testify/require"
7-
"testing"
89
)
910

1011
func Test_use_number_for_unmarshal(t *testing.T) {
@@ -45,3 +46,129 @@ func Test_read_large_number_as_interface(t *testing.T) {
4546
should.Nil(err)
4647
should.Equal(`123456789123456789123456789`, output)
4748
}
49+
50+
type caseSensitiveStruct struct {
51+
A string `json:"a"`
52+
B string `json:"b,omitempty"`
53+
C *C `json:"C,omitempty"`
54+
}
55+
56+
type C struct {
57+
D int64 `json:"D,omitempty"`
58+
E *E `json:"e,omitempty"`
59+
}
60+
61+
type E struct {
62+
F string `json:"F,omitempty"`
63+
}
64+
65+
func Test_CaseSensitive(t *testing.T) {
66+
should := require.New(t)
67+
68+
testCases := []struct {
69+
input string
70+
expectedOutput string
71+
caseSensitive bool
72+
}{
73+
{
74+
input: `{"A":"foo","B":"bar"}`,
75+
expectedOutput: `{"a":"foo","b":"bar"}`,
76+
caseSensitive: false,
77+
},
78+
{
79+
input: `{"a":"foo","b":"bar"}`,
80+
expectedOutput: `{"a":"foo","b":"bar"}`,
81+
caseSensitive: true,
82+
},
83+
{
84+
input: `{"a":"foo","b":"bar","C":{"D":10}}`,
85+
expectedOutput: `{"a":"foo","b":"bar","C":{"D":10}}`,
86+
caseSensitive: true,
87+
},
88+
{
89+
input: `{"a":"foo","B":"bar","c":{"d":10}}`,
90+
expectedOutput: `{"a":"foo"}`,
91+
caseSensitive: true,
92+
},
93+
{
94+
input: `{"a":"foo","C":{"d":10}}`,
95+
expectedOutput: `{"a":"foo","C":{}}`,
96+
caseSensitive: true,
97+
},
98+
{
99+
input: `{"a":"foo","C":{"D":10,"e":{"f":"baz"}}}`,
100+
expectedOutput: `{"a":"foo","C":{"D":10,"e":{}}}`,
101+
caseSensitive: true,
102+
},
103+
{
104+
input: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
105+
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
106+
caseSensitive: true,
107+
},
108+
{
109+
input: `{"A":"foo","c":{"d":10,"E":{"f":"baz"}}}`,
110+
expectedOutput: `{"a":"foo","C":{"D":10,"e":{"F":"baz"}}}`,
111+
caseSensitive: false,
112+
},
113+
}
114+
115+
for _, tc := range testCases {
116+
val := caseSensitiveStruct{}
117+
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
118+
should.Nil(err)
119+
120+
output, err := jsoniter.MarshalToString(val)
121+
should.Nil(err)
122+
should.Equal(tc.expectedOutput, output)
123+
}
124+
}
125+
126+
type structWithElevenFields struct {
127+
A string `json:"A,omitempty"`
128+
B string `json:"B,omitempty"`
129+
C string `json:"C,omitempty"`
130+
D string `json:"d,omitempty"`
131+
E string `json:"e,omitempty"`
132+
F string `json:"f,omitempty"`
133+
G string `json:"g,omitempty"`
134+
H string `json:"h,omitempty"`
135+
I string `json:"i,omitempty"`
136+
J string `json:"j,omitempty"`
137+
K string `json:"k,omitempty"`
138+
}
139+
140+
func Test_CaseSensitive_MoreThanTenFields(t *testing.T) {
141+
should := require.New(t)
142+
143+
testCases := []struct {
144+
input string
145+
expectedOutput string
146+
caseSensitive bool
147+
}{
148+
{
149+
input: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
150+
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6","g":"7","h":"8","i":"9","j":"10","k":"11"}`,
151+
caseSensitive: true,
152+
},
153+
{
154+
input: `{"a":"1","b":"2","c":"3","D":"4","E":"5","F":"6"}`,
155+
expectedOutput: `{"A":"1","B":"2","C":"3","d":"4","e":"5","f":"6"}`,
156+
caseSensitive: false,
157+
},
158+
{
159+
input: `{"A":"1","b":"2","d":"4","E":"5"}`,
160+
expectedOutput: `{"A":"1","d":"4"}`,
161+
caseSensitive: true,
162+
},
163+
}
164+
165+
for _, tc := range testCases {
166+
val := structWithElevenFields{}
167+
err := jsoniter.Config{CaseSensitive: tc.caseSensitive}.Froze().UnmarshalFromString(tc.input, &val)
168+
should.Nil(err)
169+
170+
output, err := jsoniter.MarshalToString(val)
171+
should.Nil(err)
172+
should.Equal(tc.expectedOutput, output)
173+
}
174+
}

reflect_struct_decoder.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
3232
for k, binding := range bindings {
3333
fields[k] = binding.Decoder.(*structFieldDecoder)
3434
}
35-
for k, binding := range bindings {
36-
if _, found := fields[strings.ToLower(k)]; !found {
37-
fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
35+
36+
if !ctx.caseSensitive() {
37+
for k, binding := range bindings {
38+
if _, found := fields[strings.ToLower(k)]; !found {
39+
fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
40+
}
3841
}
3942
}
43+
4044
return createStructDecoder(ctx, typ, fields)
4145
}
4246

@@ -47,6 +51,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
4751
knownHash := map[int64]struct{}{
4852
0: {},
4953
}
54+
5055
switch len(fields) {
5156
case 0:
5257
return &skipObjectDecoder{typ}
@@ -514,13 +519,13 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It
514519
fieldBytes := iter.ReadStringAsSlice()
515520
field = *(*string)(unsafe.Pointer(&fieldBytes))
516521
fieldDecoder = decoder.fields[field]
517-
if fieldDecoder == nil {
522+
if fieldDecoder == nil && !iter.cfg.caseSensitive {
518523
fieldDecoder = decoder.fields[strings.ToLower(field)]
519524
}
520525
} else {
521526
field = iter.ReadString()
522527
fieldDecoder = decoder.fields[field]
523-
if fieldDecoder == nil {
528+
if fieldDecoder == nil && !iter.cfg.caseSensitive {
524529
fieldDecoder = decoder.fields[strings.ToLower(field)]
525530
}
526531
}

0 commit comments

Comments
 (0)