github.com/gogf/gf@v1.16.9/util/gconv/gconv_maptomap.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gconv 8 9 import ( 10 "github.com/gogf/gf/errors/gcode" 11 "github.com/gogf/gf/errors/gerror" 12 "github.com/gogf/gf/internal/json" 13 "reflect" 14 ) 15 16 // MapToMap converts any map type variable `params` to another map type variable `pointer` 17 // using reflect. 18 // See doMapToMap. 19 func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error { 20 return doMapToMap(params, pointer, mapping...) 21 } 22 23 // doMapToMap converts any map type variable `params` to another map type variable `pointer`. 24 // 25 // The parameter `params` can be any type of map, like: 26 // map[string]string, map[string]struct, map[string]*struct, etc. 27 // 28 // The parameter `pointer` should be type of *map, like: 29 // map[int]string, map[string]struct, map[string]*struct, etc. 30 // 31 // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes 32 // sense only if the items of original map `params` is type struct. 33 func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { 34 // If given `params` is JSON, it then uses json.Unmarshal doing the converting. 35 switch r := params.(type) { 36 case []byte: 37 if json.Valid(r) { 38 if rv, ok := pointer.(reflect.Value); ok { 39 if rv.Kind() == reflect.Ptr { 40 return json.UnmarshalUseNumber(r, rv.Interface()) 41 } 42 } else { 43 return json.UnmarshalUseNumber(r, pointer) 44 } 45 } 46 case string: 47 if paramsBytes := []byte(r); json.Valid(paramsBytes) { 48 if rv, ok := pointer.(reflect.Value); ok { 49 if rv.Kind() == reflect.Ptr { 50 return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) 51 } 52 } else { 53 return json.UnmarshalUseNumber(paramsBytes, pointer) 54 } 55 } 56 } 57 var ( 58 paramsRv reflect.Value 59 paramsKind reflect.Kind 60 keyToAttributeNameMapping map[string]string 61 ) 62 if len(mapping) > 0 { 63 keyToAttributeNameMapping = mapping[0] 64 } 65 if v, ok := params.(reflect.Value); ok { 66 paramsRv = v 67 } else { 68 paramsRv = reflect.ValueOf(params) 69 } 70 paramsKind = paramsRv.Kind() 71 if paramsKind == reflect.Ptr { 72 paramsRv = paramsRv.Elem() 73 paramsKind = paramsRv.Kind() 74 } 75 if paramsKind != reflect.Map { 76 return doMapToMap(Map(params), pointer, mapping...) 77 } 78 // Empty params map, no need continue. 79 if paramsRv.Len() == 0 { 80 return nil 81 } 82 var pointerRv reflect.Value 83 if v, ok := pointer.(reflect.Value); ok { 84 pointerRv = v 85 } else { 86 pointerRv = reflect.ValueOf(pointer) 87 } 88 pointerKind := pointerRv.Kind() 89 for pointerKind == reflect.Ptr { 90 pointerRv = pointerRv.Elem() 91 pointerKind = pointerRv.Kind() 92 } 93 if pointerKind != reflect.Map { 94 return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind) 95 } 96 defer func() { 97 // Catch the panic, especially the reflect operation panics. 98 if exception := recover(); exception != nil { 99 if e, ok := exception.(errorStack); ok { 100 err = e 101 } else { 102 err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) 103 } 104 } 105 }() 106 var ( 107 paramsKeys = paramsRv.MapKeys() 108 pointerKeyType = pointerRv.Type().Key() 109 pointerValueType = pointerRv.Type().Elem() 110 pointerValueKind = pointerValueType.Kind() 111 dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys)) 112 ) 113 // Retrieve the true element type of target map. 114 if pointerValueKind == reflect.Ptr { 115 pointerValueKind = pointerValueType.Elem().Kind() 116 } 117 for _, key := range paramsKeys { 118 e := reflect.New(pointerValueType).Elem() 119 switch pointerValueKind { 120 case reflect.Map, reflect.Struct: 121 if err = doStruct(paramsRv.MapIndex(key).Interface(), e, keyToAttributeNameMapping, ""); err != nil { 122 return err 123 } 124 default: 125 e.Set( 126 reflect.ValueOf( 127 Convert( 128 paramsRv.MapIndex(key).Interface(), 129 pointerValueType.String(), 130 ), 131 ), 132 ) 133 } 134 dataMap.SetMapIndex( 135 reflect.ValueOf( 136 Convert( 137 key.Interface(), 138 pointerKeyType.Name(), 139 ), 140 ), 141 e, 142 ) 143 } 144 pointerRv.Set(dataMap) 145 return nil 146 }