gf/util/gconv/gconv_maptomap.go

142 lines
3.9 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gconv
import (
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/json"
"reflect"
)
// MapToMap converts any map type variable `params` to another map type variable `pointer`
// using reflect.
// See doMapToMap.
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
return doMapToMap(params, pointer, mapping...)
}
// 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.
//
// The parameter `pointer` should be type of *map, like:
// map[int]string, map[string]struct, , map[string]*struct, 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.
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
switch r := params.(type) {
case []byte:
if json.Valid(r) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(r, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(r, pointer)
}
}
case string:
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(paramsBytes, pointer)
}
}
}
var (
paramsRv reflect.Value
paramsKind reflect.Kind
)
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
paramsRv = reflect.ValueOf(params)
}
paramsKind = paramsRv.Kind()
if paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Map {
return doMapToMap(Map(params), pointer, mapping...)
}
// Empty params map, no need continue.
if paramsRv.Len() == 0 {
return nil
}
var pointerRv reflect.Value
if v, ok := pointer.(reflect.Value); ok {
pointerRv = v
} else {
pointerRv = reflect.ValueOf(pointer)
}
pointerKind := pointerRv.Kind()
for pointerKind == reflect.Ptr {
pointerRv = pointerRv.Elem()
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Map {
return gerror.Newf("pointer should be type of *map, but got:%s", pointerKind)
}
defer func() {
// Catch the panic, especially the reflect operation panics.
if exception := recover(); exception != nil {
if e, ok := exception.(errorStack); ok {
err = e
} else {
err = gerror.NewSkipf(1, "%v", exception)
}
}
}()
var (
paramsKeys = paramsRv.MapKeys()
pointerKeyType = pointerRv.Type().Key()
pointerValueType = pointerRv.Type().Elem()
pointerValueKind = pointerValueType.Kind()
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
)
// Retrieve the true element type of target map.
if pointerValueKind == reflect.Ptr {
pointerValueKind = pointerValueType.Elem().Kind()
}
for _, key := range paramsKeys {
e := reflect.New(pointerValueType).Elem()
switch pointerValueKind {
case reflect.Map, reflect.Struct:
if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
return err
}
default:
e.Set(
reflect.ValueOf(
Convert(
paramsRv.MapIndex(key).Interface(),
pointerValueType.String(),
),
),
)
}
dataMap.SetMapIndex(
reflect.ValueOf(
Convert(
key.Interface(),
pointerKeyType.Name(),
),
),
e,
)
}
pointerRv.Set(dataMap)
return nil
}