fix(util/gconv): one parameter to same tag in multiple struct attributes mapping failed (#3822)

This commit is contained in:
helloteemo
2024-10-14 21:34:39 +08:00
committed by GitHub
parent 4b5f637651
commit 5288b70567
2 changed files with 86 additions and 26 deletions

View File

@ -707,3 +707,53 @@ func doTestIssue3800(t *testing.T) {
t.Assert(structL.StructA.UpdatedTick, structK.Master.UpdatedTick)
})
}
// https://github.com/gogf/gf/issues/3821
func Test_Issue3821(t *testing.T) {
// Scan
gtest.C(t, func(t *gtest.T) {
var record = map[string]interface{}{
`user_id`: 1,
`user_name`: "teemo",
}
type DoubleInnerUser struct {
UserId int64 `orm:"user_id"`
}
type InnerUser struct {
UserId int32 `orm:"user_id"`
UserIdBool bool `orm:"user_id"`
Username *string `orm:"user_name"`
Username2 *string `orm:"user_name"`
Username3 string `orm:"username"`
*DoubleInnerUser
}
type User struct {
InnerUser
UserId int `orm:"user_id"`
UserIdBool gtype.Bool `orm:"user_id"`
Username string `orm:"user_name"`
Username2 string `orm:"user_name"`
Username3 *string `orm:"user_name"`
Username4 string `orm:"username"` // empty string
}
var user = &User{}
err := gconv.StructTag(record, user, "orm")
t.AssertNil(err)
t.AssertEQ(user.UserId, 1)
t.AssertEQ(user.UserIdBool.Val(), true)
t.AssertEQ(user.Username, "teemo")
t.AssertEQ(user.Username2, "teemo")
t.AssertEQ(*user.Username3, "teemo")
t.AssertEQ(user.Username4, "")
t.AssertEQ(user.InnerUser.UserId, int32(1))
t.AssertEQ(user.InnerUser.UserIdBool, true)
t.AssertEQ(*user.InnerUser.Username, "teemo")
t.AssertEQ(*user.InnerUser.Username2, "teemo")
t.AssertEQ(user.InnerUser.Username3, "")
t.AssertEQ(user.DoubleInnerUser.UserId, int64(1))
})
}

View File

@ -39,34 +39,44 @@ func (csi *CachedStructInfo) GetFieldInfo(fieldName string) *CachedFieldInfo {
}
func (csi *CachedStructInfo) AddField(field reflect.StructField, fieldIndexes []int, priorityTags []string) {
alreadyExistFieldInfo, ok := csi.tagOrFiledNameToFieldInfoMap[field.Name]
if !ok {
cachedFieldInfo := csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags)
for _, tagOrFieldName := range cachedFieldInfo.PriorityTagAndFieldName {
newFieldInfo := &CachedFieldInfo{
CachedFieldInfoBase: cachedFieldInfo.CachedFieldInfoBase,
IsField: tagOrFieldName == field.Name,
}
csi.tagOrFiledNameToFieldInfoMap[tagOrFieldName] = newFieldInfo
if newFieldInfo.IsField {
csi.FieldConvertInfos = append(csi.FieldConvertInfos, newFieldInfo)
}
}
return
}
// If the field name and type are the same
if alreadyExistFieldInfo.StructField.Type == field.Type {
alreadyExistFieldInfo.OtherSameNameField = append(
alreadyExistFieldInfo.OtherSameNameField,
csi.copyCachedInfoWithFieldIndexes(alreadyExistFieldInfo, fieldIndexes),
tagOrFieldNameArray := csi.genPriorityTagAndFieldName(field, priorityTags)
for _, tagOrFieldName := range tagOrFieldNameArray {
cachedFieldInfo, found := csi.tagOrFiledNameToFieldInfoMap[tagOrFieldName]
newFieldInfo := csi.makeOrCopyCachedInfo(
field, fieldIndexes, priorityTags, cachedFieldInfo, tagOrFieldName,
)
return
if newFieldInfo.IsField {
csi.FieldConvertInfos = append(csi.FieldConvertInfos, newFieldInfo)
}
// if the field info by `tagOrFieldName` already cached,
// it so adds this new field info to other same name field.
if found {
cachedFieldInfo.OtherSameNameField = append(cachedFieldInfo.OtherSameNameField, newFieldInfo)
} else {
csi.tagOrFiledNameToFieldInfoMap[tagOrFieldName] = newFieldInfo
}
}
// If the types are different, some information needs to be reset
alreadyExistFieldInfo.OtherSameNameField = append(
alreadyExistFieldInfo.OtherSameNameField,
csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags),
)
}
func (csi *CachedStructInfo) makeOrCopyCachedInfo(
field reflect.StructField,
fieldIndexes []int,
priorityTags []string,
cachedFieldInfo *CachedFieldInfo,
currTagOrFieldName string,
) (newFieldInfo *CachedFieldInfo) {
if cachedFieldInfo == nil {
// If the field is not cached, it creates a new one.
newFieldInfo = csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags)
} else if cachedFieldInfo.StructField.Type != field.Type {
// If the types are different, some information needs to be reset.
newFieldInfo = csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags)
} else {
// If the field types are the same.
newFieldInfo = csi.copyCachedInfoWithFieldIndexes(cachedFieldInfo, fieldIndexes)
}
newFieldInfo.IsField = currTagOrFieldName == field.Name
return
}
// copyCachedInfoWithFieldIndexes copies and returns a new CachedFieldInfo based on given CachedFieldInfo, but different