add complicated map with custom type converting support for package gconv (#2769)

This commit is contained in:
John Guo
2023-07-17 10:06:06 +08:00
committed by GitHub
parent 498b72f75a
commit 41c0dde9bf
3 changed files with 78 additions and 24 deletions

View File

@ -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\"]}}"}}`,
)
})
}

View File

@ -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
}

View File

@ -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