From 24a377d3a878565664d53eac5df1310f80f43162 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 19 Nov 2020 14:52:19 +0800 Subject: [PATCH] fix issue in custom mapping for gconv.Struct --- database/gdb/gdb_z_mysql_internal_test.go | 24 ++++++++ util/gconv/gconv_struct.go | 60 +++++++++----------- util/gconv/gconv_z_unit_struct_slice_test.go | 23 ++++++++ util/gconv/gconv_z_unit_struct_test.go | 22 +++++++ 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 74b4d12a0..aae26ec2e 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -9,6 +9,7 @@ package gdb import ( "fmt" "github.com/go-sql-driver/mysql" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -312,3 +313,26 @@ func Test_isSubQuery(t *testing.T) { t.Assert(isSubQuery("select 1"), true) }) } + +func TestResult_Structs1(t *testing.T) { + type A struct { + Id int `orm:"id"` + } + type B struct { + *A + Name string + } + gtest.C(t, func(t *gtest.T) { + r := Result{ + Record{"id": gvar.New(nil), "name": gvar.New("john")}, + Record{"id": gvar.New(nil), "name": gvar.New("smith")}, + } + array := make([]*B, 2) + err := r.Structs(&array) + t.Assert(err, nil) + t.Assert(array[0].Id, 0) + t.Assert(array[1].Id, 0) + t.Assert(array[0].Name, "john") + t.Assert(array[1].Name, "smith") + }) +} diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 83a20a800..7df12641f 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -148,18 +148,6 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str // doneMap is used to check repeated converting, its key is the real attribute name // of the struct. doneMap := make(map[string]struct{}) - // It first checks the passed mapping rules. - if len(mapping) > 0 && len(mapping[0]) > 0 { - for mapK, mapV := range mapping[0] { - // mapV is the the attribute name of the struct. - if paramV, ok := paramsMap[mapK]; ok { - doneMap[mapV] = struct{}{} - if err := bindVarToStructAttr(elem, mapV, paramV, mapping...); err != nil { - return err - } - } - } - } // 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. @@ -176,7 +164,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str if !utils.IsLetterUpper(elemFieldType.Name[0]) { continue } - // Maybe it's struct/*struct. + // Maybe it's struct/*struct embedded. if elemFieldType.Anonymous { elemFieldValue = elem.Field(i) // Ignore the interface attribute if it's nil. @@ -215,35 +203,43 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str ) for mapK, mapV := range paramsMap { attrName = "" - checkName = utils.RemoveSymbols(mapK) - // Loop to find the matched attribute name with or without - // string cases and chars like '-'/'_'/'.'/' '. - - // Matching the parameters to struct tag names. - // The is the attribute name of the struct. - for attrKey, cmpKey := range tagMap { - if strings.EqualFold(checkName, cmpKey) { - attrName = attrKey - break + // It firstly checks the passed mapping rules. + if len(mapping) > 0 && len(mapping[0]) > 0 { + if passedAttrKey, ok := mapping[0][mapK]; ok { + attrName = passedAttrKey } } - - // Matching the parameters to struct attributes. + // It secondly checks the predefined tags and matching rules. if attrName == "" { - for attrKey, cmpKey := range attrMap { - // Eg: - // UserName eq user_name - // User-Name eq username - // username eq userName - // etc. + checkName = utils.RemoveSymbols(mapK) + // Loop to find the matched attribute name with or without + // string cases and chars like '-'/'_'/'.'/' '. + + // Matching the parameters to struct tag names. + // The is the attribute name of the struct. + for attrKey, cmpKey := range tagMap { if strings.EqualFold(checkName, cmpKey) { attrName = attrKey break } } + // Matching the parameters to struct attributes. + if attrName == "" { + for attrKey, cmpKey := range attrMap { + // Eg: + // UserName eq user_name + // User-Name eq username + // username eq userName + // etc. + if strings.EqualFold(checkName, cmpKey) { + attrName = attrKey + break + } + } + } } - // No matching, give up this attribute converting. + // No matching, it gives up this attribute converting. if attrName == "" { continue } diff --git a/util/gconv/gconv_z_unit_struct_slice_test.go b/util/gconv/gconv_z_unit_struct_slice_test.go index 4fd4d9da9..d10482acf 100644 --- a/util/gconv/gconv_z_unit_struct_slice_test.go +++ b/util/gconv/gconv_z_unit_struct_slice_test.go @@ -174,3 +174,26 @@ func Test_Structs_DirectReflectSet(t *testing.T) { t.AssertEQ(a, b) }) } + +func Test_Structs_SliceIntAttribute(t *testing.T) { + type A struct { + Id []int + } + type B struct { + *A + Name string + } + gtest.C(t, func(t *gtest.T) { + var ( + array []*B + ) + err := gconv.Structs(g.Slice{ + g.Map{"id": nil, "name": "john"}, + g.Map{"id": nil, "name": "smith"}, + }, &array) + t.Assert(err, nil) + t.Assert(len(array), 2) + t.Assert(array[0].Name, "john") + t.Assert(array[1].Name, "smith") + }) +} diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index fb7146a14..8141fa26c 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1035,3 +1035,25 @@ func Test_Struct_DirectReflectSet(t *testing.T) { t.AssertEQ(a, b) }) } + +func Test_Struct_NilEmbeddedStructAttribute(t *testing.T) { + type A struct { + Name string + } + type B struct { + *A + Id int + } + + gtest.C(t, func(t *gtest.T) { + var ( + b *B + ) + err := gconv.Struct(g.Map{ + "id": 1, + "name": nil, + }, &b) + t.Assert(err, nil) + g.Dump(b) + }) +}