From 24ee5341ec758269ff8715bad2f54751b453069f Mon Sep 17 00:00:00 2001 From: Hunk Date: Thu, 17 Aug 2023 20:59:16 +0800 Subject: [PATCH] Fix empty map or struct convert to another map will return error. (#2863) --- util/gconv/gconv_map.go | 10 +++--- util/gconv/gconv_struct.go | 2 +- util/gconv/gconv_z_unit_struct_test.go | 50 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 68729483bfd..9e3605cb58f 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -29,7 +29,7 @@ const ( // tags that will be detected, otherwise it detects the tags in order of: // gconv, json, field name. func Map(value interface{}, tags ...string) map[string]interface{} { - return doMapConvert(value, recursiveTypeAuto, tags...) + return doMapConvert(value, recursiveTypeAuto, false, tags...) } // MapDeep does Map function recursively, which means if the attribute of `value` @@ -37,14 +37,14 @@ func Map(value interface{}, tags ...string) map[string]interface{} { // a map[string]interface{} type variable. // Also see Map. func MapDeep(value interface{}, tags ...string) map[string]interface{} { - return doMapConvert(value, recursiveTypeTrue, tags...) + return doMapConvert(value, recursiveTypeTrue, false, tags...) } // doMapConvert implements the map converting. // It automatically checks and converts json string to map if `value` is string/[]byte. // // TODO completely implement the recursive converting for all types, especially the map. -func doMapConvert(value interface{}, recursive recursiveType, tags ...string) map[string]interface{} { +func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, tags ...string) map[string]interface{} { if value == nil { return nil } @@ -209,6 +209,7 @@ func doMapConvert(value interface{}, recursive recursiveType, tags ...string) ma RecursiveType: recursive, RecursiveOption: recursive == recursiveTypeTrue, Tags: newTags, + MustMapReturn: mustMapReturn, }, ) if m, ok := convertedValue.(map[string]interface{}); ok { @@ -228,6 +229,7 @@ type doMapConvertForMapOrStructValueInput struct { RecursiveType recursiveType // The type from top function entry. RecursiveOption bool // Whether convert recursively for `current` operation. Tags []string // Map key mapping. + MustMapReturn bool // Must return map instead of Value when empty. } func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} { @@ -468,7 +470,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in } } } - if len(dataMap) == 0 { + if !in.MustMapReturn && len(dataMap) == 0 { return in.Value } return dataMap diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index fea556a4156..0acfb26473a 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -206,7 +206,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // paramsMap is the map[string]interface{} type variable for params. // DO NOT use MapDeep here. - paramsMap := Map(paramsInterface) + paramsMap := doMapConvert(paramsInterface, recursiveTypeAuto, true) if paramsMap == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 156b66c4400..e3d7c2a15d7 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1349,3 +1349,53 @@ func Test_Struct_WithCustomType(t *testing.T) { t.Assert(*req2.PayMode, 1000) }) } + +func Test_Struct_EmptyStruct(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var err error + + type StructA struct { + } + + type StructB struct { + } + + var s1 StructA + var s2 *StructB + + err = gconv.Scan(s1, &s2) + t.AssertNil(err) + + err = gconv.Scan(&s1, &s2) + t.AssertNil(err) + + type StructC struct { + Val int `json:"val,omitempty"` + } + + type StructD struct { + Val int + } + + var s3 StructC + var s4 *StructD + + err = gconv.Scan(s3, &s4) + t.AssertNil(err) + t.Assert(s4.Val, 0) + + err = gconv.Scan(&s3, &s4) + t.AssertNil(err) + t.Assert(s4.Val, 0) + + s3.Val = 123 + err = gconv.Scan(s3, &s4) + t.AssertNil(err) + t.Assert(s4.Val, 123) + + err = gconv.Scan(&s3, &s4) + t.AssertNil(err) + t.Assert(s4.Val, 123) + + }) +}