From 41c0dde9bf12550ad2f1cc77def20ea233d25fef Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 Jul 2023 10:06:06 +0800 Subject: [PATCH] add complicated map with custom type converting support for package gconv (#2769) --- ...ghttp_z_unit_feature_router_strict_test.go | 45 +++++++++++++++++++ util/gconv/gconv_convert.go | 19 +++++--- util/gconv/gconv_maptomap.go | 38 ++++++++-------- 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/net/ghttp/ghttp_z_unit_feature_router_strict_test.go b/net/ghttp/ghttp_z_unit_feature_router_strict_test.go index 2ae99a638..74d82fda7 100644 --- a/net/ghttp/ghttp_z_unit_feature_router_strict_test.go +++ b/net/ghttp/ghttp_z_unit_feature_router_strict_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" @@ -253,3 +254,47 @@ func Test_Issue1708(t *testing.T) { ) }) } + +func Test_Custom_Slice_Type_Attribute(t *testing.T) { + type ( + WhiteListKey string + WhiteListValues []string + WhiteList map[WhiteListKey]WhiteListValues + ) + type Req struct { + Id int + List WhiteList + } + type Res struct { + Content string + } + + s := g.Server(guid.S()) + s.Use(ghttp.MiddlewareHandlerResponse) + s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) { + return &Res{ + Content: gjson.MustEncodeString(req), + }, nil + }) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) + content := ` +{ + "id":1, + "list":{ + "key": ["a", "b", "c"] + } +} +` + t.Assert( + client.PostContent(ctx, "/test", content), + `{"code":0,"message":"","data":{"Content":"{\"Id\":1,\"List\":{\"key\":[\"a\",\"b\",\"c\"]}}"}}`, + ) + }) +} diff --git a/util/gconv/gconv_convert.go b/util/gconv/gconv_convert.go index a5c6e99fc..d3d352c29 100644 --- a/util/gconv/gconv_convert.go +++ b/util/gconv/gconv_convert.go @@ -28,7 +28,7 @@ func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{ type doConvertInput struct { FromValue interface{} // Value that is converted from. ToTypeName string // Target value type name in string. - ReferValue interface{} // Referred value, a value in type `ToTypeName`. + ReferValue interface{} // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value. Extra []interface{} // Extra values for implementing the converting. // Marks that the value is already converted and set to `ReferValue`. Caller can ignore the returned result. // It is an attribute for internal usage purpose. @@ -254,9 +254,7 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { default: if in.ReferValue != nil { - var ( - referReflectValue reflect.Value - ) + var referReflectValue reflect.Value if v, ok := in.ReferValue.(reflect.Value); ok { referReflectValue = v } else { @@ -272,7 +270,8 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { } } }() - if referReflectValue.Kind() == reflect.Ptr { + switch referReflectValue.Kind() { + case reflect.Ptr: // Type converting for custom type pointers. // Eg: // type PayMode int @@ -294,11 +293,19 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { in.alreadySetToReferValue = true return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface() } + + case reflect.Map: + var targetValue = reflect.New(referReflectValue.Type()).Elem() + if err := doMapToMap(in.FromValue, targetValue); err == nil { + in.alreadySetToReferValue = true + } + return targetValue.Interface() } in.ToTypeName = referReflectValue.Kind().String() in.ReferValue = nil in.alreadySetToReferValue = true - return reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface() + convertedValue = reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface() + return convertedValue } return in.FromValue } diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index 51eb0efda..7ff924cca 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -24,10 +24,10 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str // doMapToMap converts any map type variable `params` to another map type variable `pointer`. // // The parameter `params` can be any type of map, like: -// map[string]string, map[string]struct, map[string]*struct, etc. +// map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc. // // The parameter `pointer` should be type of *map, like: -// map[int]string, map[string]struct, map[string]*struct, etc. +// map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc. // // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes // sense only if the items of original map `params` is type struct. @@ -95,7 +95,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind) } defer func() { - // Catch the panic, especially the reflect operation panics. + // Catch the panic, especially the reflection operation panics. if exception := recover(); exception != nil { if v, ok := exception.(error); ok && gerror.HasStack(v) { err = v @@ -116,31 +116,33 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s pointerValueKind = pointerValueType.Elem().Kind() } for _, key := range paramsKeys { - e := reflect.New(pointerValueType).Elem() + mapValue := reflect.New(pointerValueType).Elem() switch pointerValueKind { case reflect.Map, reflect.Struct: - if err = doStruct(paramsRv.MapIndex(key).Interface(), e, keyToAttributeNameMapping, ""); err != nil { + if err = doStruct(paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, ""); err != nil { return err } default: - e.Set( + mapValue.Set( reflect.ValueOf( - Convert( - paramsRv.MapIndex(key).Interface(), - pointerValueType.String(), - ), + doConvert(doConvertInput{ + FromValue: paramsRv.MapIndex(key).Interface(), + ToTypeName: pointerValueType.String(), + ReferValue: mapValue, + Extra: nil, + }), ), ) } - dataMap.SetMapIndex( - reflect.ValueOf( - Convert( - key.Interface(), - pointerKeyType.Name(), - ), - ), - e, + var mapKey = reflect.ValueOf( + doConvert(doConvertInput{ + FromValue: key.Interface(), + ToTypeName: pointerKeyType.Name(), + ReferValue: reflect.New(pointerKeyType).Elem().Interface(), + Extra: nil, + }), ) + dataMap.SetMapIndex(mapKey, mapValue) } pointerRv.Set(dataMap) return nil