1
0
mirror of https://github.com/json-iterator/go.git synced 2025-06-15 22:50:24 +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

@ -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
}
}