1
0
mirror of https://github.com/json-iterator/go.git synced 2024-11-24 08:22:14 +02:00

fix #191 do not always assume the object field is simple string

This commit is contained in:
Tao Wen 2017-10-31 22:38:41 +08:00
parent fbd210edfc
commit f1258b01aa
6 changed files with 124 additions and 41 deletions

View File

@ -110,9 +110,7 @@ type Encoder struct {
// Encode encode interface{} as JSON to io.Writer // Encode encode interface{} as JSON to io.Writer
func (adapter *Encoder) Encode(val interface{}) error { func (adapter *Encoder) Encode(val interface{}) error {
adapter.stream.WriteVal(val) adapter.stream.WriteVal(val)
if adapter.stream.cfg.addNewline { adapter.stream.WriteRaw("\n")
adapter.stream.WriteRaw("\n")
}
adapter.stream.Flush() adapter.stream.Flush()
return adapter.stream.Error return adapter.stream.Error
} }

View File

@ -12,26 +12,26 @@ import (
// Config customize how the API should behave. // Config customize how the API should behave.
// The API is created from Config by Froze. // The API is created from Config by Froze.
type Config struct { type Config struct {
IndentionStep int IndentionStep int
MarshalFloatWith6Digits bool MarshalFloatWith6Digits bool
EscapeHTML bool EscapeHTML bool
SortMapKeys bool SortMapKeys bool
UseNumber bool UseNumber bool
TagKey string TagKey string
ValidateJsonRawMessage bool ValidateJsonRawMessage bool
AddNewline bool ObjectFieldMustBeSimpleString bool
} }
type frozenConfig struct { type frozenConfig struct {
configBeforeFrozen Config configBeforeFrozen Config
sortMapKeys bool sortMapKeys bool
addNewline bool indentionStep int
indentionStep int objectFieldMustBeSimpleString bool
decoderCache unsafe.Pointer decoderCache unsafe.Pointer
encoderCache unsafe.Pointer encoderCache unsafe.Pointer
extensions []Extension extensions []Extension
streamPool chan *Stream streamPool chan *Stream
iteratorPool chan *Iterator iteratorPool chan *Iterator
} }
// API the public interface of this package. // API the public interface of this package.
@ -60,24 +60,24 @@ var ConfigCompatibleWithStandardLibrary = Config{
EscapeHTML: true, EscapeHTML: true,
SortMapKeys: true, SortMapKeys: true,
ValidateJsonRawMessage: true, ValidateJsonRawMessage: true,
AddNewline: true,
}.Froze() }.Froze()
// ConfigFastest marshals float with only 6 digits precision // ConfigFastest marshals float with only 6 digits precision
var ConfigFastest = Config{ var ConfigFastest = Config{
EscapeHTML: false, EscapeHTML: false,
MarshalFloatWith6Digits: true, MarshalFloatWith6Digits: true, // will lose precession
ObjectFieldMustBeSimpleString: true, // do not unescape object field
}.Froze() }.Froze()
// Froze forge API from config // Froze forge API from config
func (cfg Config) Froze() API { func (cfg Config) Froze() API {
// TODO: cache frozen config // TODO: cache frozen config
frozenConfig := &frozenConfig{ frozenConfig := &frozenConfig{
sortMapKeys: cfg.SortMapKeys, sortMapKeys: cfg.SortMapKeys,
indentionStep: cfg.IndentionStep, indentionStep: cfg.IndentionStep,
addNewline: cfg.AddNewline, objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
streamPool: make(chan *Stream, 16), streamPool: make(chan *Stream, 16),
iteratorPool: make(chan *Iterator, 16), iteratorPool: make(chan *Iterator, 16),
} }
atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{})) atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{})) atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))

View File

@ -19,7 +19,16 @@ func (iter *Iterator) ReadObject() (ret string) {
c = iter.nextToken() c = iter.nextToken()
if c == '"' { if c == '"' {
iter.unreadByte() iter.unreadByte()
return string(iter.readObjectFieldAsBytes()) if iter.cfg.objectFieldMustBeSimpleString {
return string(iter.readObjectFieldAsBytes())
} else {
field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
}
} }
if c == '}' { if c == '}' {
return "" // end of object return "" // end of object
@ -27,7 +36,16 @@ func (iter *Iterator) ReadObject() (ret string) {
iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c})) iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
return return
case ',': case ',':
return string(iter.readObjectFieldAsBytes()) if iter.cfg.objectFieldMustBeSimpleString {
return string(iter.readObjectFieldAsBytes())
} else {
field := iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
return field
}
case '}': case '}':
return "" // end of object return "" // end of object
default: default:
@ -44,17 +62,34 @@ func (iter *Iterator) readFieldHash() int32 {
for i := iter.head; i < iter.tail; i++ { for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape // require ascii string and no escape
b := iter.buf[i] b := iter.buf[i]
if 'A' <= b && b <= 'Z' { if !iter.cfg.objectFieldMustBeSimpleString && b == '\\' {
b += 'a' - 'A' iter.head = i
for _, b := range iter.readStringSlowPath() {
if 'A' <= b && b <= 'Z' {
b += 'a' - 'A'
}
hash ^= int64(b)
hash *= 0x1000193
}
c = iter.nextToken()
if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
}
return int32(hash)
} }
if b == '"' { if b == '"' {
iter.head = i + 1 iter.head = i + 1
c = iter.nextToken() c = iter.nextToken()
if c != ':' { if c != ':' {
iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
return 0
} }
return int32(hash) return int32(hash)
} }
if 'A' <= b && b <= 'Z' {
b += 'a' - 'A'
}
hash ^= int64(b) hash ^= int64(b)
hash *= 0x1000193 hash *= 0x1000193
} }
@ -80,18 +115,38 @@ func calcHash(str string) int32 {
// ReadObjectCB read object with callback, the key is ascii only and field name not copied // ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken() c := iter.nextToken()
var fieldBytes []byte
var field string
if c == '{' { if c == '{' {
c = iter.nextToken() c = iter.nextToken()
if c == '"' { if c == '"' {
iter.unreadByte() iter.unreadByte()
field := iter.readObjectFieldAsBytes() if iter.cfg.objectFieldMustBeSimpleString {
if !callback(iter, *(*string)(unsafe.Pointer(&field))) { fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes))
} else {
field = iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
}
if !callback(iter, field) {
return false return false
} }
c = iter.nextToken() c = iter.nextToken()
for c == ',' { for c == ',' {
field = iter.readObjectFieldAsBytes() if iter.cfg.objectFieldMustBeSimpleString {
if !callback(iter, *(*string)(unsafe.Pointer(&field))) { fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes))
} else {
field = iter.ReadString()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
}
if !callback(iter, field) {
return false return false
} }
c = iter.nextToken() c = iter.nextToken()

View File

@ -427,8 +427,18 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
if !iter.readObjectStart() { if !iter.readObjectStart() {
return return
} }
fieldBytes := iter.readObjectFieldAsBytes() var fieldBytes []byte
field := *(*string)(unsafe.Pointer(&fieldBytes)) var field string
if iter.cfg.objectFieldMustBeSimpleString {
fieldBytes = iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes))
} else {
field = iter.ReadString()
c := iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
}
fieldDecoder := decoder.fields[strings.ToLower(field)] fieldDecoder := decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()
@ -436,8 +446,16 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
fieldDecoder.Decode(ptr, iter) fieldDecoder.Decode(ptr, iter)
} }
for iter.nextToken() == ',' { for iter.nextToken() == ',' {
fieldBytes = iter.readObjectFieldAsBytes() if iter.cfg.objectFieldMustBeSimpleString {
field = *(*string)(unsafe.Pointer(&fieldBytes)) fieldBytes := iter.readObjectFieldAsBytes()
field = *(*string)(unsafe.Pointer(&fieldBytes))
} else {
field = iter.ReadString()
c := iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
}
fieldDecoder = decoder.fields[strings.ToLower(field)] fieldDecoder = decoder.fields[strings.ToLower(field)]
if fieldDecoder == nil { if fieldDecoder == nil {
iter.Skip() iter.Skip()

View File

@ -22,7 +22,7 @@ func Test_new_encoder(t *testing.T) {
encoder2 := NewEncoder(buf2) encoder2 := NewEncoder(buf2)
encoder2.SetEscapeHTML(false) encoder2.SetEscapeHTML(false)
encoder2.Encode([]int{1}) encoder2.Encode([]int{1})
should.Equal("[1]", buf2.String()) should.Equal("[1]\n", buf2.String())
} }
func Test_string_encode_with_std_without_html_escape(t *testing.T) { func Test_string_encode_with_std_without_html_escape(t *testing.T) {

View File

@ -328,3 +328,15 @@ func Test_decode_nested(t *testing.T) {
t.Fatal(slice[2]) t.Fatal(slice[2])
} }
} }
func Test_decode_field_with_escape(t *testing.T) {
should := require.New(t)
type TestObject struct {
Field1 string
}
var obj TestObject
should.Nil(ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(`{"Field\"1":"hello"}`), &obj))
should.Equal("", obj.Field1)
should.Nil(ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(`{"\u0046ield1":"hello"}`), &obj))
should.Equal("hello", obj.Field1)
}