1
0
mirror of https://github.com/json-iterator/go.git synced 2025-03-20 20:54:55 +02:00

#63 keep struct field order

This commit is contained in:
Tao Wen 2017-06-23 08:21:02 +08:00
parent d7ea1acd3f
commit 8f8e16b4c2
6 changed files with 72 additions and 47 deletions

View File

@ -49,7 +49,7 @@ var ConfigCompatibleWithStandardLibrary = Config{
}.Froze()
var ConfigFastest = Config{
EscapeHtml: false,
EscapeHtml: false,
MarshalFloatWith6Digits: true,
}.Froze()
@ -144,7 +144,6 @@ func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
}
func (cfg *frozenConfig) escapeHtml() {
// for better performance
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
}
@ -192,14 +191,14 @@ func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
func (cfg *frozenConfig) cleanDecoders() {
typeDecoders = map[string]ValDecoder{}
fieldDecoders = map[string]ValDecoder{}
atomic.StorePointer(&cfg.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
*cfg = *cfg.configBeforeFrozen.Froze()
}
// cleanEncoders cleans encoders registered or cached
func (cfg *frozenConfig) cleanEncoders() {
typeEncoders = map[string]ValEncoder{}
fieldEncoders = map[string]ValEncoder{}
atomic.StorePointer(&cfg.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
*cfg = *cfg.configBeforeFrozen.Froze()
}
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {

View File

@ -192,7 +192,8 @@ func _getTypeEncoderFromExtension(typ reflect.Type) ValEncoder {
}
func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, error) {
anonymousBindings := []*Binding{}
headAnonymousBindings := []*Binding{}
tailAnonymousBindings := []*Binding{}
bindings := []*Binding{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
@ -205,7 +206,11 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
for _, binding := range structDescriptor.Fields {
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, false}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
anonymousBindings = append(anonymousBindings, binding)
if field.Offset == 0 {
headAnonymousBindings = append(headAnonymousBindings, binding)
} else {
tailAnonymousBindings = append(tailAnonymousBindings, binding)
}
}
} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
structDescriptor, err := describeStruct(cfg, field.Type.Elem())
@ -217,7 +222,11 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, false}
binding.Decoder = &optionalDecoder{field.Type, binding.Decoder}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
anonymousBindings = append(anonymousBindings, binding)
if field.Offset == 0 {
headAnonymousBindings = append(headAnonymousBindings, binding)
} else {
tailAnonymousBindings = append(tailAnonymousBindings, binding)
}
}
}
} else {
@ -276,7 +285,8 @@ func describeStruct(cfg *frozenConfig, typ reflect.Type) (*StructDescriptor, err
binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty}
}
// insert anonymous bindings to the head
structDescriptor.Fields = append(anonymousBindings, structDescriptor.Fields...)
structDescriptor.Fields = append(headAnonymousBindings, structDescriptor.Fields...)
structDescriptor.Fields = append(structDescriptor.Fields, tailAnonymousBindings...)
return structDescriptor, nil
}

View File

@ -8,20 +8,35 @@ import (
)
func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
fields := map[string]*structFieldEncoder{}
fieldsByToName := map[string]int{}
orderedFields := []*structFieldTo{}
structDescriptor, err := describeStruct(cfg, typ)
if err != nil {
return nil, err
}
for _, binding := range structDescriptor.Fields {
for index, binding := range structDescriptor.Fields {
for _, toName := range binding.ToNames {
fields[toName] = binding.Encoder.(*structFieldEncoder)
oldIndex, found := fieldsByToName[toName]
if found {
orderedFields[oldIndex] = nil // replaced by the later one
}
fieldsByToName[toName] = index
orderedFields = append(orderedFields, &structFieldTo{
binding.Encoder.(*structFieldEncoder),
toName,
})
}
}
if len(fields) == 0 {
if len(orderedFields) == 0 {
return &emptyStructEncoder{}, nil
}
return &structEncoder{fields}, nil
finalOrderedFields := []structFieldTo{}
for _, structFieldTo := range orderedFields {
if structFieldTo != nil {
finalOrderedFields = append(finalOrderedFields, *structFieldTo)
}
}
return &structEncoder{finalOrderedFields}, nil
}
func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error) {
@ -977,21 +992,26 @@ func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
}
type structEncoder struct {
fields map[string]*structFieldEncoder
fields []structFieldTo
}
type structFieldTo struct {
encoder *structFieldEncoder
toName string
}
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
stream.WriteObjectStart()
isNotFirst := false
for fieldName, field := range encoder.fields {
if field.omitempty && field.IsEmpty(ptr) {
for _, field := range encoder.fields {
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
continue
}
if isNotFirst {
stream.WriteMore()
}
stream.WriteObjectField(fieldName)
field.Encode(ptr, stream)
stream.WriteObjectField(field.toName)
field.encoder.Encode(ptr, stream)
isNotFirst = true
}
stream.WriteObjectEnd()
@ -1001,23 +1021,23 @@ func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {
var encoderToUse ValEncoder
encoderToUse = encoder
if len(encoder.fields) == 1 {
var firstField *structFieldEncoder
var firstFieldName string
for fieldName, field := range encoder.fields {
firstFieldName = fieldName
firstField = field
}
firstFieldName := encoder.fields[0].toName
firstField := encoder.fields[0].encoder
firstEncoder := firstField.fieldEncoder
firstEncoderName := reflect.TypeOf(firstEncoder).String()
// interface{} has inline optimization for this case
if firstEncoderName == "*jsoniter.optionalEncoder" {
encoderToUse = &structEncoder{
fields: map[string]*structFieldEncoder{
firstFieldName: {
field: firstField.field,
fieldEncoder: firstEncoder.(*optionalEncoder).valueEncoder,
omitempty: firstField.omitempty,
}},
fields: []structFieldTo{
{
toName: firstFieldName,
encoder: &structFieldEncoder{
field: firstField.field,
fieldEncoder: firstEncoder.(*optionalEncoder).valueEncoder,
omitempty: firstField.omitempty,
},
},
},
}
}
}
@ -1026,7 +1046,7 @@ func (encoder *structEncoder) EncodeInterface(val interface{}, stream *Stream) {
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
for _, field := range encoder.fields {
if !field.IsEmpty(ptr) {
if !field.encoder.IsEmpty(ptr) {
return false
}
}

View File

@ -252,7 +252,7 @@ func (stream *Stream) WriteStringWithHtmlEscaped(s string) {
func writeStringSlowPathWithHtmlEscaped(stream *Stream, i int, s string, valLen int) {
start := i
// for the remaining parts, we process them char by char
for ; i < valLen; {
for i < valLen {
if b := s[i]; b < utf8.RuneSelf {
if htmlSafeSet[b] {
i++
@ -351,7 +351,7 @@ func (stream *Stream) WriteString(s string) {
func writeStringSlowPath(stream *Stream, i int, s string, valLen int) {
start := i
// for the remaining parts, we process them char by char
for ; i < valLen; {
for i < valLen {
if b := s[i]; b < utf8.RuneSelf {
if safeSet[b] {
i++

View File

@ -349,10 +349,7 @@ func Test_multiple_level_anonymous_struct(t *testing.T) {
obj := Level3{Level2{Level1{"1"}, "2"}, "3"}
output, err := MarshalToString(obj)
should.Nil(err)
fmt.Println(output)
should.Contains(output, `"Field1":"1"`)
should.Contains(output, `"Field2":"2"`)
should.Contains(output, `"Field3":"3"`)
should.Equal(`{"Field1":"1","Field2":"2","Field3":"3"}`, output)
}
func Test_multiple_level_anonymous_struct_with_ptr(t *testing.T) {
@ -413,7 +410,7 @@ func Test_embed_at_last(t *testing.T) {
}
type Struct struct {
Field string `json:"field"`
Field string `json:"field"`
FieldType string `json:"field_type"`
Base
}
@ -421,9 +418,7 @@ func Test_embed_at_last(t *testing.T) {
s := Struct{Field: "field", FieldType: "field_type", Base: Base{"type"}}
output, err := MarshalToString(s)
should.Nil(err)
should.Contains(output, `"type":"type"`)
should.Contains(output, `"field":"field"`)
should.Contains(output, `"field_type":"field_type"`)
should.Equal(`{"field":"field","field_type":"field_type","type":"type"}`, output)
}
func Test_decode_nested(t *testing.T) {

View File

@ -138,28 +138,29 @@ func Test_string_encode_with_std_without_html_escape(t *testing.T) {
func Test_unicode(t *testing.T) {
should := require.New(t)
output , _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
output, _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
should.Equal(`{"a":"数字山谷"}`, output)
output , _ = Config{EscapeHtml: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
output, _ = Config{EscapeHtml: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
should.Equal(`{"a":"数字山谷"}`, output)
}
func Test_unicode_and_escape(t *testing.T) {
should := require.New(t)
output , err := MarshalToString(`"数字山谷"`)
output, err := MarshalToString(`"数字山谷"`)
should.Nil(err)
should.Equal(`"\"数字山谷\""`, output)
output , err = ConfigFastest.MarshalToString(`"数字山谷"`)
output, err = ConfigFastest.MarshalToString(`"数字山谷"`)
should.Nil(err)
should.Equal(`"\"数字山谷\""`, output)
}
func Test_unsafe_unicode(t *testing.T) {
ConfigDefault.cleanEncoders()
should := require.New(t)
output , err := MarshalToString("he\u2029\u2028he")
output, err := ConfigDefault.MarshalToString("he\u2029\u2028he")
should.Nil(err)
should.Equal(`"he\u2029\u2028he"`, output)
output , err = ConfigFastest.MarshalToString("he\u2029\u2028he")
output, err = ConfigFastest.MarshalToString("he\u2029\u2028he")
should.Nil(err)
should.Equal("\"he\u2029\u2028he\"", output)
}