2019-02-02 16:18:25 +08:00
|
|
|
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-11-30 09:48:57 +08:00
|
|
|
//
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-11-30 09:48:57 +08:00
|
|
|
|
|
|
|
package gconv
|
|
|
|
|
|
|
|
import (
|
2020-05-04 23:42:51 +08:00
|
|
|
"github.com/gogf/gf/errors/gerror"
|
2020-06-17 21:16:25 +08:00
|
|
|
"github.com/gogf/gf/internal/json"
|
2019-05-28 21:41:00 +08:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
2018-11-30 09:48:57 +08:00
|
|
|
|
2019-07-29 21:01:19 +08:00
|
|
|
"github.com/gogf/gf/internal/empty"
|
2020-03-11 23:17:41 +08:00
|
|
|
"github.com/gogf/gf/internal/utils"
|
2019-05-16 13:56:49 +08:00
|
|
|
)
|
2019-05-28 21:41:00 +08:00
|
|
|
|
2019-12-10 21:14:15 +08:00
|
|
|
// Map converts any variable <value> to map[string]interface{}. If the parameter <value> is not a
|
|
|
|
// map/struct/*struct type, then the conversion will fail and returns nil.
|
2019-07-04 11:11:41 +08:00
|
|
|
//
|
|
|
|
// If <value> is a struct/*struct object, the second parameter <tags> specifies the most priority
|
2020-02-08 14:07:32 +08:00
|
|
|
// tags that will be detected, otherwise it detects the tags in order of:
|
|
|
|
// gconv, json, field name.
|
2019-06-19 09:06:52 +08:00
|
|
|
func Map(value interface{}, tags ...string) map[string]interface{} {
|
2020-01-02 19:45:41 +08:00
|
|
|
return doMapConvert(value, false, tags...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MapDeep does Map function recursively, which means if the attribute of <value>
|
|
|
|
// is also a struct/*struct, calls Map function on this attribute converting it to
|
|
|
|
// a map[string]interface{} type variable.
|
|
|
|
// Also see Map.
|
|
|
|
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
|
|
|
|
return doMapConvert(value, true, tags...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// doMapConvert implements the map converting.
|
2020-03-08 23:09:37 +08:00
|
|
|
// It automatically checks and converts json string to map if <value> is string/[]byte.
|
2020-06-15 18:59:18 +08:00
|
|
|
//
|
|
|
|
// TODO completely implement the recursive converting for all types.
|
2020-01-02 19:45:41 +08:00
|
|
|
func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} {
|
2019-06-19 09:06:52 +08:00
|
|
|
if value == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
|
|
|
|
// Assert the common combination of types, and finally it uses reflection.
|
|
|
|
m := make(map[string]interface{})
|
|
|
|
switch r := value.(type) {
|
|
|
|
case string:
|
2020-07-08 10:52:45 +08:00
|
|
|
// If it is a JSON string, automatically unmarshal it!
|
2020-04-07 21:29:41 +08:00
|
|
|
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
|
|
|
if err := json.Unmarshal([]byte(r), &m); err != nil {
|
2020-01-20 21:25:55 +08:00
|
|
|
return nil
|
2020-01-20 19:56:42 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
case []byte:
|
2020-07-08 10:52:45 +08:00
|
|
|
// If it is a JSON string, automatically unmarshal it!
|
2020-04-07 21:29:41 +08:00
|
|
|
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
|
|
|
if err := json.Unmarshal(r, &m); err != nil {
|
2020-01-20 21:25:55 +08:00
|
|
|
return nil
|
2020-01-20 19:56:42 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
case map[interface{}]interface{}:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[interface{}]string:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[interface{}]int:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[interface{}]uint:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[interface{}]float32:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[interface{}]float64:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[string]bool:
|
|
|
|
for k, v := range r {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
case map[string]int:
|
|
|
|
for k, v := range r {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
case map[string]uint:
|
|
|
|
for k, v := range r {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
case map[string]float32:
|
|
|
|
for k, v := range r {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
case map[string]float64:
|
|
|
|
for k, v := range r {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
case map[string]interface{}:
|
|
|
|
return r
|
|
|
|
case map[int]interface{}:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[int]string:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
|
|
|
case map[uint]string:
|
|
|
|
for k, v := range r {
|
|
|
|
m[String(k)] = v
|
|
|
|
}
|
2020-07-08 10:52:45 +08:00
|
|
|
|
2020-04-07 21:29:41 +08:00
|
|
|
default:
|
2020-07-08 10:52:45 +08:00
|
|
|
// Not a common type, it then uses reflection for conversion.
|
2020-04-30 16:53:47 +08:00
|
|
|
var rv reflect.Value
|
|
|
|
if v, ok := value.(reflect.Value); ok {
|
|
|
|
rv = v
|
|
|
|
} else {
|
|
|
|
rv = reflect.ValueOf(value)
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
kind := rv.Kind()
|
|
|
|
// If it is a pointer, we should find its real data type.
|
|
|
|
if kind == reflect.Ptr {
|
|
|
|
rv = rv.Elem()
|
|
|
|
kind = rv.Kind()
|
|
|
|
}
|
|
|
|
switch kind {
|
|
|
|
// If <value> is type of array, it converts the value of even number index as its key and
|
2020-07-08 10:52:45 +08:00
|
|
|
// the value of odd number index as its corresponding value, for example:
|
2020-04-07 21:29:41 +08:00
|
|
|
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
|
2020-07-08 10:52:45 +08:00
|
|
|
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
2020-04-07 21:29:41 +08:00
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
length := rv.Len()
|
|
|
|
for i := 0; i < length; i += 2 {
|
|
|
|
if i+1 < length {
|
|
|
|
m[String(rv.Index(i).Interface())] = rv.Index(i + 1).Interface()
|
|
|
|
} else {
|
|
|
|
m[String(rv.Index(i).Interface())] = nil
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
case reflect.Map:
|
|
|
|
ks := rv.MapKeys()
|
|
|
|
for _, k := range ks {
|
|
|
|
m[String(k.Interface())] = rv.MapIndex(k).Interface()
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
case reflect.Struct:
|
|
|
|
// Map converting interface check.
|
|
|
|
if v, ok := value.(apiMapStrAny); ok {
|
|
|
|
return v.MapStrAny()
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
// Using reflect for converting.
|
2020-04-09 13:37:27 +08:00
|
|
|
var (
|
|
|
|
rtField reflect.StructField
|
|
|
|
rvField reflect.Value
|
|
|
|
rvKind reflect.Kind
|
|
|
|
rt = rv.Type()
|
|
|
|
name = ""
|
|
|
|
tagArray = structTagPriority
|
|
|
|
)
|
2020-04-07 21:29:41 +08:00
|
|
|
switch len(tags) {
|
|
|
|
case 0:
|
|
|
|
// No need handle.
|
|
|
|
case 1:
|
|
|
|
tagArray = append(strings.Split(tags[0], ","), structTagPriority...)
|
|
|
|
default:
|
|
|
|
tagArray = append(tags, structTagPriority...)
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
for i := 0; i < rv.NumField(); i++ {
|
|
|
|
rtField = rt.Field(i)
|
|
|
|
rvField = rv.Field(i)
|
|
|
|
// Only convert the public attributes.
|
|
|
|
fieldName := rtField.Name
|
|
|
|
if !utils.IsLetterUpper(fieldName[0]) {
|
|
|
|
continue
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
name = ""
|
|
|
|
fieldTag := rtField.Tag
|
|
|
|
for _, tag := range tagArray {
|
|
|
|
if name = fieldTag.Get(tag); name != "" {
|
|
|
|
break
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
if name == "" {
|
2020-04-09 13:37:27 +08:00
|
|
|
name = fieldName
|
2020-04-07 21:29:41 +08:00
|
|
|
} else {
|
|
|
|
// Support json tag feature: -, omitempty
|
|
|
|
name = strings.TrimSpace(name)
|
|
|
|
if name == "-" {
|
2019-06-19 09:06:52 +08:00
|
|
|
continue
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
array := strings.Split(name, ",")
|
|
|
|
if len(array) > 1 {
|
|
|
|
switch strings.TrimSpace(array[1]) {
|
|
|
|
case "omitempty":
|
|
|
|
if empty.IsEmpty(rvField.Interface()) {
|
|
|
|
continue
|
|
|
|
} else {
|
2019-06-19 09:06:52 +08:00
|
|
|
name = strings.TrimSpace(array[0])
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
default:
|
|
|
|
name = strings.TrimSpace(array[0])
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
}
|
|
|
|
if recursive {
|
|
|
|
rvKind = rvField.Kind()
|
|
|
|
if rvKind == reflect.Ptr {
|
|
|
|
rvField = rvField.Elem()
|
2020-01-03 20:23:10 +08:00
|
|
|
rvKind = rvField.Kind()
|
2020-04-07 21:29:41 +08:00
|
|
|
}
|
|
|
|
if rvKind == reflect.Struct {
|
2020-04-09 15:34:23 +08:00
|
|
|
hasNoTag := name == fieldName
|
|
|
|
if hasNoTag && rtField.Anonymous {
|
2020-04-09 13:37:27 +08:00
|
|
|
// It means this attribute field has no tag.
|
|
|
|
// Overwrite the attribute with sub-struct attribute fields.
|
|
|
|
for k, v := range doMapConvert(rvField.Interface(), recursive, tags...) {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// It means this attribute field has desired tag.
|
|
|
|
m[name] = doMapConvert(rvField.Interface(), recursive, tags...)
|
2020-01-02 19:45:41 +08:00
|
|
|
}
|
2020-04-09 13:37:27 +08:00
|
|
|
|
2020-01-03 20:23:10 +08:00
|
|
|
} else {
|
2020-04-20 22:36:28 +08:00
|
|
|
if rvField.IsValid() {
|
|
|
|
m[name] = rvField.Interface()
|
|
|
|
} else {
|
|
|
|
m[name] = nil
|
|
|
|
}
|
2020-01-02 19:45:41 +08:00
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
} else {
|
2020-04-20 22:36:28 +08:00
|
|
|
if rvField.IsValid() {
|
|
|
|
m[name] = rvField.Interface()
|
|
|
|
} else {
|
|
|
|
m[name] = nil
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
default:
|
|
|
|
return nil
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
|
|
|
}
|
2020-04-07 21:29:41 +08:00
|
|
|
return m
|
2018-11-30 09:48:57 +08:00
|
|
|
}
|
2019-05-28 21:41:00 +08:00
|
|
|
|
2019-12-14 17:01:27 +08:00
|
|
|
// MapStrStr converts <value> to map[string]string.
|
|
|
|
// Note that there might be data copy for this map type converting.
|
|
|
|
func MapStrStr(value interface{}, tags ...string) map[string]string {
|
|
|
|
if r, ok := value.(map[string]string); ok {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
m := Map(value, tags...)
|
|
|
|
if len(m) > 0 {
|
2020-06-03 23:44:21 +08:00
|
|
|
vMap := make(map[string]string, len(m))
|
2019-12-14 17:01:27 +08:00
|
|
|
for k, v := range m {
|
|
|
|
vMap[k] = String(v)
|
|
|
|
}
|
|
|
|
return vMap
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MapStrStrDeep converts <value> to map[string]string recursively.
|
|
|
|
// Note that there might be data copy for this map type converting.
|
|
|
|
func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
|
|
|
|
if r, ok := value.(map[string]string); ok {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
m := MapDeep(value, tags...)
|
|
|
|
if len(m) > 0 {
|
2020-06-03 23:44:21 +08:00
|
|
|
vMap := make(map[string]string, len(m))
|
2019-12-14 17:01:27 +08:00
|
|
|
for k, v := range m {
|
|
|
|
vMap[k] = String(v)
|
|
|
|
}
|
|
|
|
return vMap
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// MapToMap converts any map type variable <params> to another map type variable <pointer>
|
|
|
|
// using reflect.
|
2020-05-04 23:42:51 +08:00
|
|
|
// See doMapToMap.
|
2019-09-19 23:23:41 +08:00
|
|
|
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
|
|
|
return doMapToMap(params, pointer, false, mapping...)
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// MapToMapDeep converts any map type variable <params> to another map type variable <pointer>
|
|
|
|
// using reflect recursively.
|
2020-05-04 23:42:51 +08:00
|
|
|
// See doMapToMap.
|
2019-09-19 23:23:41 +08:00
|
|
|
func MapToMapDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
|
|
|
return doMapToMap(params, pointer, true, mapping...)
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// doMapToMap converts any map type variable <params> to another map type variable <pointer>.
|
2020-05-04 23:42:51 +08:00
|
|
|
//
|
|
|
|
// 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{}, deep bool, mapping ...map[string]string) (err error) {
|
2020-04-30 16:53:47 +08:00
|
|
|
var (
|
|
|
|
paramsRv = reflect.ValueOf(params)
|
|
|
|
paramsKind = paramsRv.Kind()
|
|
|
|
)
|
2019-07-29 19:52:05 +08:00
|
|
|
if paramsKind == reflect.Ptr {
|
|
|
|
paramsRv = paramsRv.Elem()
|
|
|
|
paramsKind = paramsRv.Kind()
|
|
|
|
}
|
|
|
|
if paramsKind != reflect.Map {
|
2020-07-13 23:13:50 +08:00
|
|
|
return gerror.New("params should be type of map")
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
2020-05-04 23:42:51 +08:00
|
|
|
// Empty params map, no need continue.
|
|
|
|
if paramsRv.Len() == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-04-30 16:53:47 +08:00
|
|
|
var pointerRv reflect.Value
|
|
|
|
if v, ok := pointer.(reflect.Value); ok {
|
|
|
|
pointerRv = v
|
|
|
|
} else {
|
|
|
|
pointerRv = reflect.ValueOf(pointer)
|
|
|
|
}
|
2019-07-29 19:52:05 +08:00
|
|
|
pointerKind := pointerRv.Kind()
|
|
|
|
for pointerKind == reflect.Ptr {
|
|
|
|
pointerRv = pointerRv.Elem()
|
|
|
|
pointerKind = pointerRv.Kind()
|
|
|
|
}
|
|
|
|
if pointerKind != reflect.Map {
|
2020-07-13 23:13:50 +08:00
|
|
|
return gerror.New("pointer should be type of *map")
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
2020-05-04 23:42:51 +08:00
|
|
|
defer func() {
|
|
|
|
// Catch the panic, especially the reflect operation panics.
|
|
|
|
if e := recover(); e != nil {
|
|
|
|
err = gerror.NewfSkip(1, "%v", e)
|
|
|
|
}
|
|
|
|
}()
|
2020-04-30 16:53:47 +08:00
|
|
|
var (
|
|
|
|
paramsKeys = paramsRv.MapKeys()
|
|
|
|
pointerKeyType = pointerRv.Type().Key()
|
|
|
|
pointerValueType = pointerRv.Type().Elem()
|
2020-05-04 23:42:51 +08:00
|
|
|
pointerValueKind = pointerValueType.Kind()
|
2020-04-30 16:53:47 +08:00
|
|
|
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
|
|
|
)
|
2020-05-04 23:42:51 +08:00
|
|
|
// Retrieve the true element type of target map.
|
|
|
|
if pointerValueKind == reflect.Ptr {
|
|
|
|
pointerValueKind = pointerValueType.Elem().Kind()
|
|
|
|
}
|
2019-07-29 19:52:05 +08:00
|
|
|
for _, key := range paramsKeys {
|
|
|
|
e := reflect.New(pointerValueType).Elem()
|
2020-05-04 23:42:51 +08:00
|
|
|
switch pointerValueKind {
|
|
|
|
case reflect.Map, reflect.Struct:
|
|
|
|
if deep {
|
|
|
|
if err = StructDeep(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
2020-05-04 23:42:51 +08:00
|
|
|
default:
|
|
|
|
e.Set(
|
|
|
|
reflect.ValueOf(
|
|
|
|
Convert(
|
|
|
|
paramsRv.MapIndex(key).Interface(),
|
|
|
|
pointerValueType.String(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
|
|
|
dataMap.SetMapIndex(
|
2020-05-04 23:42:51 +08:00
|
|
|
reflect.ValueOf(
|
|
|
|
Convert(
|
|
|
|
key.Interface(),
|
|
|
|
pointerKeyType.Name(),
|
|
|
|
),
|
|
|
|
),
|
2019-07-29 19:52:05 +08:00
|
|
|
e,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
pointerRv.Set(dataMap)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
2020-05-04 23:42:51 +08:00
|
|
|
// See doMapToMaps.
|
2019-09-19 23:23:41 +08:00
|
|
|
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
|
|
|
return doMapToMaps(params, pointer, false, mapping...)
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// MapToMapsDeep converts any map type variable <params> to another map type variable
|
2020-05-04 23:42:51 +08:00
|
|
|
// <pointer> recursively.
|
|
|
|
// See doMapToMaps.
|
2019-09-19 23:23:41 +08:00
|
|
|
func MapToMapsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
|
|
|
return doMapToMaps(params, pointer, true, mapping...)
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
|
|
|
|
2020-05-17 18:16:26 +08:00
|
|
|
// doMapToMaps converts any map type variable <params> to another map type variable <pointer>.
|
2020-05-04 23:42:51 +08:00
|
|
|
//
|
|
|
|
// The parameter <params> can be any type of map, of which the item type is slice map, like:
|
|
|
|
// map[int][]map, map[string][]map.
|
|
|
|
//
|
|
|
|
// The parameter <pointer> should be type of *map, of which the item type is slice map, like:
|
|
|
|
// map[string][]struct, map[string][]*struct.
|
|
|
|
//
|
|
|
|
// The optional parameter <mapping> is used for struct attribute to map key mapping, which makes
|
|
|
|
// sense only if the items of original map is type struct.
|
|
|
|
//
|
|
|
|
// TODO it's supposed supporting target type <pointer> like: map[int][]map, map[string][]map.
|
|
|
|
func doMapToMaps(params interface{}, pointer interface{}, deep bool, mapping ...map[string]string) (err error) {
|
2020-04-30 16:53:47 +08:00
|
|
|
var (
|
|
|
|
paramsRv = reflect.ValueOf(params)
|
|
|
|
paramsKind = paramsRv.Kind()
|
|
|
|
)
|
2019-07-29 19:52:05 +08:00
|
|
|
if paramsKind == reflect.Ptr {
|
|
|
|
paramsRv = paramsRv.Elem()
|
|
|
|
paramsKind = paramsRv.Kind()
|
|
|
|
}
|
|
|
|
if paramsKind != reflect.Map {
|
2020-07-13 23:13:50 +08:00
|
|
|
return gerror.New("params should be type of map")
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
2020-05-04 23:42:51 +08:00
|
|
|
// Empty params map, no need continue.
|
|
|
|
if paramsRv.Len() == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-04-30 16:53:47 +08:00
|
|
|
var (
|
|
|
|
pointerRv = reflect.ValueOf(pointer)
|
|
|
|
pointerKind = pointerRv.Kind()
|
|
|
|
)
|
2019-07-29 19:52:05 +08:00
|
|
|
for pointerKind == reflect.Ptr {
|
|
|
|
pointerRv = pointerRv.Elem()
|
|
|
|
pointerKind = pointerRv.Kind()
|
|
|
|
}
|
|
|
|
if pointerKind != reflect.Map {
|
2020-07-13 23:13:50 +08:00
|
|
|
return gerror.New("pointer should be type of *map/**map")
|
2019-07-29 19:52:05 +08:00
|
|
|
}
|
2020-05-04 23:42:51 +08:00
|
|
|
defer func() {
|
|
|
|
// Catch the panic, especially the reflect operation panics.
|
|
|
|
if e := recover(); e != nil {
|
|
|
|
err = gerror.NewfSkip(1, "%v", e)
|
|
|
|
}
|
|
|
|
}()
|
2020-04-30 16:53:47 +08:00
|
|
|
var (
|
|
|
|
paramsKeys = paramsRv.MapKeys()
|
|
|
|
pointerKeyType = pointerRv.Type().Key()
|
|
|
|
pointerValueType = pointerRv.Type().Elem()
|
|
|
|
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
|
|
|
)
|
2019-07-29 19:52:05 +08:00
|
|
|
for _, key := range paramsKeys {
|
2020-05-04 23:42:51 +08:00
|
|
|
e := reflect.New(pointerValueType).Elem()
|
2019-07-29 19:52:05 +08:00
|
|
|
if deep {
|
2020-05-04 23:42:51 +08:00
|
|
|
if err = StructsDeep(paramsRv.MapIndex(key).Interface(), e.Addr(), mapping...); err != nil {
|
2019-07-29 19:52:05 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2020-05-04 23:42:51 +08:00
|
|
|
if err = Structs(paramsRv.MapIndex(key).Interface(), e.Addr(), mapping...); err != nil {
|
2019-07-29 19:52:05 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dataMap.SetMapIndex(
|
2020-05-04 23:42:51 +08:00
|
|
|
reflect.ValueOf(
|
|
|
|
Convert(
|
|
|
|
key.Interface(),
|
|
|
|
pointerKeyType.Name(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
e,
|
2019-07-29 19:52:05 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
pointerRv.Set(dataMap)
|
|
|
|
return nil
|
|
|
|
}
|