improve package gconv

This commit is contained in:
John Guo
2022-04-11 21:54:23 +08:00
parent d5e5a48170
commit 55e0262c37
3 changed files with 45 additions and 31 deletions

View File

@ -87,6 +87,7 @@ func TagFields(pointer interface{}, priority []string) ([]Field, error) {
// Note that,
// 1. It only retrieves the exported attributes with first letter up-case from struct.
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
// 3. If one field has no specified tag, it uses its field name as result map key.
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
fields, err := TagFields(pointer, priority)
if err != nil {
@ -94,7 +95,11 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
}
tagMap := make(map[string]string, len(fields))
for _, field := range fields {
tagMap[field.TagValue] = field.Name()
if field.TagValue == "" {
tagMap[field.Name()] = field.Name()
} else {
tagMap[field.TagValue] = field.Name()
}
}
return tagMap, nil
}
@ -105,6 +110,7 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
// Note that,
// 1. It only retrieves the exported attributes with first letter up-case from struct.
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
// 3. If one field has no specified tag, it uses its field name as result map key.
func TagMapField(object interface{}, priority []string) (map[string]Field, error) {
fields, err := TagFields(object, priority)
if err != nil {
@ -113,7 +119,11 @@ func TagMapField(object interface{}, priority []string) (map[string]Field, error
tagMap := make(map[string]Field, len(fields))
for _, field := range fields {
tagField := field
tagMap[field.TagValue] = tagField
if field.TagValue == "" {
tagMap[tagField.Name()] = tagField
} else {
tagMap[field.TagValue] = tagField
}
}
return tagMap, nil
}
@ -174,7 +184,9 @@ exitLoop:
return fields, nil
}
func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]Field, error) {
func getFieldValuesByTagPriority(
pointer interface{}, priority []string, repeatedTagFilteringMap map[string]struct{},
) ([]Field, error) {
fields, err := getFieldValues(pointer)
if err != nil {
return nil, err
@ -199,17 +211,18 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
}
if tagValue != "" {
// Filter repeated tag.
if _, ok := tagMap[tagValue]; ok {
if _, ok := repeatedTagFilteringMap[tagValue]; ok {
continue
}
tagField := field
tagField.TagName = tagName
tagField.TagValue = tagValue
tagFields = append(tagFields, tagField)
}
tagField := field
tagField.TagName = tagName
tagField.TagValue = tagValue
tagFields = append(tagFields, tagField)
// If this is an embedded attribute, it retrieves the tags recursively.
if field.IsEmbedded() && field.OriginalKind() == reflect.Struct {
if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil {
subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, repeatedTagFilteringMap)
if err != nil {
return nil, err
} else {
tagFields = append(tagFields, subTagFields...)

View File

@ -192,11 +192,11 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
// The key of the attrMap is the attribute name of the struct,
// and the value is its replaced name for later comparison to improve performance.
var (
tempName string
elemFieldType reflect.StructField
elemFieldValue reflect.Value
elemType = pointerElemReflectValue.Type()
attrMap = make(map[string]string) // Attribute name to its check name which has no symbols.
tempName string
elemFieldType reflect.StructField
elemFieldValue reflect.Value
elemType = pointerElemReflectValue.Type()
attrToCheckNameMap = make(map[string]string)
)
for i := 0; i < pointerElemReflectValue.NumField(); i++ {
elemFieldType = elemType.Field(i)
@ -219,35 +219,35 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
} else {
tempName = elemFieldType.Name
attrMap[tempName] = utils.RemoveSymbols(tempName)
attrToCheckNameMap[tempName] = utils.RemoveSymbols(tempName)
}
}
if len(attrMap) == 0 {
if len(attrToCheckNameMap) == 0 {
return nil
}
// The key of the tagMap is the attribute name of the struct,
// and the value is its replaced tag name for later comparison to improve performance.
var (
tagMap = make(map[string]string) // Tag name to its check name which has no symbols.
priorityTagArray []string
attrToTagCheckNameMap = make(map[string]string)
priorityTagArray []string
)
if priorityTag != "" {
priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), StructTagPriority...)
} else {
priorityTagArray = StructTagPriority
}
tagToNameMap, err := gstructs.TagMapName(pointerElemReflectValue, priorityTagArray)
tagToAttrNameMap, err := gstructs.TagMapName(pointerElemReflectValue, priorityTagArray)
if err != nil {
return err
}
for tagName, attributeName := range tagToNameMap {
for tagName, attributeName := range tagToAttrNameMap {
// If there's something else in the tag string,
// it uses the first part which is split using char ','.
// Eg:
// orm:"id, priority"
// orm:"name, with:uid=id"
tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0])
attrToTagCheckNameMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0])
// If tag and attribute values both exist in `paramsMap`,
// it then uses the tag value overwriting the attribute value in `paramsMap`.
if paramsMap[tagName] != nil && paramsMap[attributeName] != nil {
@ -259,26 +259,27 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
attrName string
checkName string
)
for mapK, mapV := range paramsMap {
for paramName, paramValue := range paramsMap {
attrName = ""
// It firstly checks the passed mapping rules.
if len(mapping) > 0 {
if passedAttrKey, ok := mapping[mapK]; ok {
if passedAttrKey, ok := mapping[paramName]; ok {
attrName = passedAttrKey
}
}
// It secondly checks the predefined tags and matching rules.
if attrName == "" {
// It firstly considers `mapK` as accurate tag name, and retrieve attribute name from `tagToNameMap` .
attrName = tagToNameMap[mapK]
// It firstly considers `paramName` as accurate tag name,
// and retrieve attribute name from `tagToAttrNameMap` .
attrName = tagToAttrNameMap[paramName]
if attrName == "" {
checkName = utils.RemoveSymbols(mapK)
checkName = utils.RemoveSymbols(paramName)
// Loop to find the matched attribute name with or without
// string cases and chars like '-'/'_'/'.'/' '.
// Matching the parameters to struct tag names.
// The `attrKey` is the attribute name of the struct.
for attrKey, cmpKey := range tagMap {
for attrKey, cmpKey := range attrToTagCheckNameMap {
if strings.EqualFold(checkName, cmpKey) {
attrName = attrKey
break
@ -288,7 +289,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
// Matching the parameters to struct attributes.
if attrName == "" {
for attrKey, cmpKey := range attrMap {
for attrKey, cmpKey := range attrToCheckNameMap {
// Eg:
// UserName eq user_name
// User-Name eq username
@ -312,7 +313,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
}
// Mark it done.
doneMap[attrName] = struct{}{}
if err = bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping); err != nil {
if err = bindVarToStructAttr(pointerElemReflectValue, attrName, paramValue, mapping); err != nil {
return err
}
}

View File

@ -1270,8 +1270,8 @@ func Test_Struct_Issue1563(t *testing.T) {
user := new(User)
params2 := g.Map{
"password1": "111",
"PASS1": "222",
"Pass1": "333",
//"PASS1": "222",
"Pass1": "333",
}
if err := gconv.Struct(params2, user); err == nil {
t.Assert(user.Pass1, `111`)