mirror of https://github.com/goccy/go-json.git
Fix conflicted anonymous fields
This commit is contained in:
parent
a718a9a1ef
commit
7ada1b2467
|
@ -617,58 +617,14 @@ func (e *Encoder) optimizeAnonymousFields(head *structFieldCode) {
|
|||
type structFieldPair struct {
|
||||
prevField *structFieldCode
|
||||
curField *structFieldCode
|
||||
isTaggedKey bool
|
||||
linked bool
|
||||
}
|
||||
|
||||
func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) {
|
||||
if code := e.compiledCode(typ, withIndent); code != nil {
|
||||
return code, nil
|
||||
}
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
compiled := &compiledCode{}
|
||||
if withIndent {
|
||||
e.structTypeToCompiledIndentCode[typeptr] = compiled
|
||||
} else {
|
||||
e.structTypeToCompiledCode[typeptr] = compiled
|
||||
}
|
||||
// header => code => structField => code => end
|
||||
// ^ |
|
||||
// |__________|
|
||||
fieldNum := typ.NumField()
|
||||
fieldIdx := 0
|
||||
var (
|
||||
head *structFieldCode
|
||||
code *opcode
|
||||
prevField *structFieldCode
|
||||
)
|
||||
e.indent++
|
||||
tags := structTags{}
|
||||
anonymousFields := map[string]structFieldPair{}
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if isIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
tags = append(tags, structTagFromField(field))
|
||||
}
|
||||
for i, tag := range tags {
|
||||
field := tag.field
|
||||
fieldType := type2rtype(field.Type)
|
||||
if isPtr && i == 0 {
|
||||
// head field of pointer structure at top level
|
||||
// if field type is pointer and implements MarshalJSON or MarshalText,
|
||||
// it need to operation of dereference of pointer.
|
||||
if field.Type.Kind() == reflect.Ptr &&
|
||||
(field.Type.Implements(marshalJSONType) || field.Type.Implements(marshalTextType)) {
|
||||
fieldType = rtype_ptrTo(fieldType)
|
||||
}
|
||||
}
|
||||
valueCode, err := e.compile(fieldType, false, withIndent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anonymous {
|
||||
f := valueCode.toStructFieldCode()
|
||||
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *structFieldCode) map[string][]structFieldPair {
|
||||
//fmt.Println("type = ", typ, "valueCode = ", valueCode.dump())
|
||||
anonymousFields := map[string][]structFieldPair{}
|
||||
f := valueCode
|
||||
var prevAnonymousField *structFieldCode
|
||||
for {
|
||||
existsKey := tags.existsKey(f.displayKey)
|
||||
|
@ -694,27 +650,15 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
continue
|
||||
}
|
||||
|
||||
// conflict anonymous fields
|
||||
if existsFieldSet, exists := anonymousFields[f.displayKey]; exists {
|
||||
if !existsFieldSet.linked {
|
||||
if existsFieldSet.prevField == nil {
|
||||
// head operation
|
||||
existsFieldSet.curField.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(existsFieldSet.prevField, existsFieldSet.curField)
|
||||
}
|
||||
existsFieldSet.linked = true
|
||||
}
|
||||
if prevAnonymousField == nil {
|
||||
// head operation
|
||||
f.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(prevAnonymousField, f)
|
||||
}
|
||||
}
|
||||
anonymousFields[f.displayKey] = structFieldPair{
|
||||
anonymousFields[f.displayKey] = append(anonymousFields[f.displayKey], structFieldPair{
|
||||
prevField: prevAnonymousField,
|
||||
curField: f,
|
||||
isTaggedKey: f.isTaggedKey,
|
||||
})
|
||||
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next.toStructFieldCode()) {
|
||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||
}
|
||||
}
|
||||
if f.nextField == nil {
|
||||
break
|
||||
|
@ -722,6 +666,102 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
prevAnonymousField = f
|
||||
f = f.nextField.toStructFieldCode()
|
||||
}
|
||||
return anonymousFields
|
||||
}
|
||||
|
||||
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
|
||||
for _, fieldPairs := range anonymousFields {
|
||||
if len(fieldPairs) == 1 {
|
||||
continue
|
||||
}
|
||||
// conflict anonymous fields
|
||||
taggedPairs := []structFieldPair{}
|
||||
for _, fieldPair := range fieldPairs {
|
||||
if fieldPair.isTaggedKey {
|
||||
taggedPairs = append(taggedPairs, fieldPair)
|
||||
} else {
|
||||
if !fieldPair.linked {
|
||||
if fieldPair.prevField == nil {
|
||||
// head operation
|
||||
fieldPair.curField.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
|
||||
}
|
||||
fieldPair.linked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(taggedPairs) > 1 {
|
||||
for _, fieldPair := range taggedPairs {
|
||||
if !fieldPair.linked {
|
||||
if fieldPair.prevField == nil {
|
||||
// head operation
|
||||
fieldPair.curField.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
|
||||
}
|
||||
fieldPair.linked = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, fieldPair := range taggedPairs {
|
||||
fieldPair.curField.isTaggedKey = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) {
|
||||
if code := e.compiledCode(typ, withIndent); code != nil {
|
||||
return code, nil
|
||||
}
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
compiled := &compiledCode{}
|
||||
if withIndent {
|
||||
e.structTypeToCompiledIndentCode[typeptr] = compiled
|
||||
} else {
|
||||
e.structTypeToCompiledCode[typeptr] = compiled
|
||||
}
|
||||
// header => code => structField => code => end
|
||||
// ^ |
|
||||
// |__________|
|
||||
fieldNum := typ.NumField()
|
||||
fieldIdx := 0
|
||||
var (
|
||||
head *structFieldCode
|
||||
code *opcode
|
||||
prevField *structFieldCode
|
||||
)
|
||||
e.indent++
|
||||
tags := structTags{}
|
||||
anonymousFields := map[string][]structFieldPair{}
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if isIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
tags = append(tags, structTagFromField(field))
|
||||
}
|
||||
for i, tag := range tags {
|
||||
field := tag.field
|
||||
fieldType := type2rtype(field.Type)
|
||||
if isPtr && i == 0 {
|
||||
// head field of pointer structure at top level
|
||||
// if field type is pointer and implements MarshalJSON or MarshalText,
|
||||
// it need to operation of dereference of pointer.
|
||||
if field.Type.Kind() == reflect.Ptr &&
|
||||
(field.Type.Implements(marshalJSONType) || field.Type.Implements(marshalTextType)) {
|
||||
fieldType = rtype_ptrTo(fieldType)
|
||||
}
|
||||
}
|
||||
valueCode, err := e.compile(fieldType, false, withIndent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anonymous {
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode.toStructFieldCode()) {
|
||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||
}
|
||||
}
|
||||
if fieldNum == 1 && valueCode.op == opPtr {
|
||||
// if field number is one and primitive pointer type,
|
||||
|
@ -742,6 +782,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
},
|
||||
anonymousKey: field.Anonymous,
|
||||
key: []byte(key),
|
||||
isTaggedKey: tag.isTaggedKey,
|
||||
displayKey: tag.key,
|
||||
offset: field.Offset,
|
||||
}
|
||||
|
@ -794,6 +835,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
head.end = structEndCode
|
||||
code.next = structEndCode
|
||||
|
||||
e.optimizeConflictAnonymousFields(anonymousFields)
|
||||
e.optimizeAnonymousFields(head)
|
||||
|
||||
ret := (*opcode)(unsafe.Pointer(head))
|
||||
|
|
|
@ -306,6 +306,7 @@ type structFieldCode struct {
|
|||
*opcodeHeader
|
||||
key []byte
|
||||
displayKey string
|
||||
isTaggedKey bool
|
||||
offset uintptr
|
||||
anonymousKey bool
|
||||
nextField *opcode
|
||||
|
@ -352,6 +353,7 @@ func (c *structFieldCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
}
|
||||
field := &structFieldCode{
|
||||
key: c.key,
|
||||
isTaggedKey: c.isTaggedKey,
|
||||
displayKey: c.displayKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
offset: c.offset,
|
||||
|
|
|
@ -23,6 +23,7 @@ func isIgnoredStructField(field reflect.StructField) bool {
|
|||
|
||||
type structTag struct {
|
||||
key string
|
||||
isTaggedKey bool
|
||||
isOmitEmpty bool
|
||||
isString bool
|
||||
field reflect.StructField
|
||||
|
@ -42,13 +43,15 @@ func (t structTags) existsKey(key string) bool {
|
|||
func structTagFromField(field reflect.StructField) *structTag {
|
||||
keyName := field.Name
|
||||
tag := getTag(field)
|
||||
st := &structTag{field: field}
|
||||
opts := strings.Split(tag, ",")
|
||||
if len(opts) > 0 {
|
||||
if opts[0] != "" {
|
||||
keyName = opts[0]
|
||||
st.isTaggedKey = true
|
||||
}
|
||||
}
|
||||
st := &structTag{key: keyName, field: field}
|
||||
st.key = keyName
|
||||
if len(opts) > 1 {
|
||||
st.isOmitEmpty = opts[1] == "omitempty"
|
||||
st.isString = opts[1] == "string"
|
||||
|
|
Loading…
Reference in New Issue