You've already forked json-iterator
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:
@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user