You've already forked json-iterator
							
							
				mirror of
				https://github.com/json-iterator/go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	merge
This commit is contained in:
		| @@ -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) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -2,12 +2,13 @@ package jsoniter | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"github.com/modern-go/concurrent" | ||||
| 	"github.com/modern-go/reflect2" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"github.com/modern-go/concurrent" | ||||
| 	"github.com/modern-go/reflect2" | ||||
| ) | ||||
|  | ||||
| // Config customize how the API should behave. | ||||
| @@ -23,6 +24,7 @@ type Config struct { | ||||
| 	OnlyTaggedField               bool | ||||
| 	ValidateJsonRawMessage        bool | ||||
| 	ObjectFieldMustBeSimpleString bool | ||||
| 	CaseSensitive                 bool | ||||
| } | ||||
|  | ||||
| // API the public interface of this package. | ||||
| @@ -75,6 +77,7 @@ type frozenConfig struct { | ||||
| 	extensions                    []Extension | ||||
| 	streamPool                    *sync.Pool | ||||
| 	iteratorPool                  *sync.Pool | ||||
| 	caseSensitive                 bool | ||||
| } | ||||
|  | ||||
| func (cfg *frozenConfig) initCache() { | ||||
| @@ -128,6 +131,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{} { | ||||
|   | ||||
| @@ -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,14 @@ func (iter *Iterator) readFieldHash() int64 { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func calcHash(str string) int64 { | ||||
| func calcHash(str string, caseSensitive bool) int64 { | ||||
| 	hash := int64(0x811c9dc5) | ||||
| 	for _, b := range str { | ||||
| 		hash ^= int64(unicode.ToLower(b)) | ||||
| 		if caseSensitive { | ||||
| 			hash ^= int64(b) | ||||
| 		} else { | ||||
| 			hash ^= int64(unicode.ToLower(b)) | ||||
| 		} | ||||
| 		hash *= 0x1000193 | ||||
| 	} | ||||
| 	return int64(hash) | ||||
|   | ||||
							
								
								
									
										11
									
								
								reflect.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								reflect.go
									
									
									
									
									
								
							| @@ -2,9 +2,10 @@ package jsoniter | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/modern-go/reflect2" | ||||
| 	"reflect" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"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, | ||||
|   | ||||
| @@ -2,10 +2,11 @@ package jsoniter | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/modern-go/reflect2" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"github.com/modern-go/reflect2" | ||||
| ) | ||||
|  | ||||
| func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { | ||||
| @@ -31,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) | ||||
| } | ||||
|  | ||||
| @@ -46,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} | ||||
| @@ -65,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} | ||||
| @@ -88,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} | ||||
| @@ -119,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} | ||||
| @@ -156,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} | ||||
| @@ -199,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} | ||||
| @@ -248,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} | ||||
| @@ -303,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} | ||||
| @@ -364,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} | ||||
| @@ -431,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} | ||||
| @@ -513,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)] | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user