From 3830516ed0efb2a55f64eb22cf7894fecd8aea4c Mon Sep 17 00:00:00 2001 From: Nikhita Raghunath Date: Mon, 11 Jun 2018 15:35:33 +0530 Subject: [PATCH] Fix case sensitivity for nested fields --- api_tests/config_test.go | 129 +++++++++++++++++++++++++++++++++++++- reflect_struct_decoder.go | 15 +++-- 2 files changed, 138 insertions(+), 6 deletions(-) diff --git a/api_tests/config_test.go b/api_tests/config_test.go index d3e93e5..7aa349b 100644 --- a/api_tests/config_test.go +++ b/api_tests/config_test.go @@ -2,9 +2,10 @@ package test import ( "encoding/json" + "testing" + "github.com/json-iterator/go" "github.com/stretchr/testify/require" - "testing" ) func Test_use_number_for_unmarshal(t *testing.T) { @@ -45,3 +46,129 @@ func Test_read_large_number_as_interface(t *testing.T) { should.Nil(err) should.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) + } +} diff --git a/reflect_struct_decoder.go b/reflect_struct_decoder.go index c76cecd..355d2d1 100644 --- a/reflect_struct_decoder.go +++ b/reflect_struct_decoder.go @@ -32,11 +32,15 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { for k, binding := range bindings { fields[k] = binding.Decoder.(*structFieldDecoder) } - for k, binding := range bindings { - if _, found := fields[strings.ToLower(k)]; !found { - fields[strings.ToLower(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) } @@ -47,6 +51,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF knownHash := map[int64]struct{}{ 0: {}, } + switch len(fields) { case 0: return &skipObjectDecoder{typ} @@ -514,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)] } }