diff --git a/util/gconv/gconv_z_unit_issue_test.go b/util/gconv/gconv_z_unit_issue_test.go index dbb361e2873..00ff12a4c95 100644 --- a/util/gconv/gconv_z_unit_issue_test.go +++ b/util/gconv/gconv_z_unit_issue_test.go @@ -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)) + }) +} diff --git a/util/gconv/internal/structcache/structcache_cached_struct_info.go b/util/gconv/internal/structcache/structcache_cached_struct_info.go index c04ea8ac1c0..70fd1bf4419 100644 --- a/util/gconv/internal/structcache/structcache_cached_struct_info.go +++ b/util/gconv/internal/structcache/structcache_cached_struct_info.go @@ -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, - } + tagOrFieldNameArray := csi.genPriorityTagAndFieldName(field, priorityTags) + for _, tagOrFieldName := range tagOrFieldNameArray { + cachedFieldInfo, found := csi.tagOrFiledNameToFieldInfoMap[tagOrFieldName] + newFieldInfo := csi.makeOrCopyCachedInfo( + field, fieldIndexes, priorityTags, cachedFieldInfo, tagOrFieldName, + ) + 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 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), - ) - return +} + +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) } - // If the types are different, some information needs to be reset - alreadyExistFieldInfo.OtherSameNameField = append( - alreadyExistFieldInfo.OtherSameNameField, - csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags), - ) + newFieldInfo.IsField = currTagOrFieldName == field.Name + return } // copyCachedInfoWithFieldIndexes copies and returns a new CachedFieldInfo based on given CachedFieldInfo, but different