mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
add complicated map with custom type converting support for package gconv (#2769)
This commit is contained in:
@ -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\"]}}"}}`,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user