From 55e0262c376bc75e5f835da005440c953835ba9b Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 11 Apr 2022 21:54:23 +0800 Subject: [PATCH] improve package gconv --- os/gstructs/gstructs_tag.go | 31 +++++++++++++------ util/gconv/gconv_struct.go | 41 +++++++++++++------------- util/gconv/gconv_z_unit_struct_test.go | 4 +-- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/os/gstructs/gstructs_tag.go b/os/gstructs/gstructs_tag.go index a59b59945..f545eb17e 100644 --- a/os/gstructs/gstructs_tag.go +++ b/os/gstructs/gstructs_tag.go @@ -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...) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 2de206c59..541da86bb 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -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 } } diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index d48050313..ea9c29e9a 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -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`)