github.com/zhongdalu/gf@v1.0.0/g/util/gconv/gconv_struct.go (about) 1 // Copyright 2017-2019 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf. 6 7 package gconv 8 9 import ( 10 "errors" 11 "fmt" 12 "reflect" 13 "strings" 14 15 "github.com/zhongdalu/gf/g/internal/structs" 16 "github.com/zhongdalu/gf/g/internal/strutils" 17 ) 18 19 // Struct maps the params key-value pairs to the corresponding struct object's properties. 20 // The third parameter <mapping> is unnecessary, indicating the mapping rules between the custom key name 21 // and the attribute name(case sensitive). 22 // 23 // Note: 24 // 1. The <params> can be any type of map/struct, usually a map. 25 // 2. The second parameter <pointer> should be a pointer to the struct object. 26 // 3. Only the public attributes of struct object can be mapped. 27 // 4. If <params> is a map, the key of the map <params> can be lowercase. 28 // It will automatically convert the first letter of the key to uppercase 29 // in mapping procedure to do the matching. 30 // It ignores the map key, if it does not match. 31 func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) error { 32 if params == nil { 33 return errors.New("params cannot be nil") 34 } 35 if pointer == nil { 36 return errors.New("object pointer cannot be nil") 37 } 38 paramsMap := Map(params) 39 if paramsMap == nil { 40 return fmt.Errorf("invalid params: %v", params) 41 } 42 // Using reflect to do the converting, 43 // it also supports type of reflect.Value for <pointer>(always in internal usage). 44 elem, ok := pointer.(reflect.Value) 45 if !ok { 46 rv := reflect.ValueOf(pointer) 47 if kind := rv.Kind(); kind != reflect.Ptr { 48 return fmt.Errorf("object pointer should be type of: %v", kind) 49 } 50 // Using IsNil on reflect.Ptr variable is OK. 51 if !rv.IsValid() || rv.IsNil() { 52 return errors.New("object pointer cannot be nil") 53 } 54 elem = rv.Elem() 55 // Auto create struct object. 56 // For example, if <pointer> is **User, then <elem> is *User, which is a pointer to User. 57 if elem.Type().Kind() == reflect.Ptr && (!elem.IsValid() || elem.IsNil()) { 58 e := reflect.New(elem.Type().Elem()).Elem() 59 elem.Set(e.Addr()) 60 elem = e 61 } 62 } 63 // It only performs one converting to the same attribute. 64 // doneMap is used to check repeated converting. 65 doneMap := make(map[string]bool) 66 // It first checks the passed mapping rules. 67 if len(mapping) > 0 && len(mapping[0]) > 0 { 68 for mapK, mapV := range mapping[0] { 69 if v, ok := paramsMap[mapK]; ok { 70 doneMap[mapV] = true 71 if err := bindVarToStructAttr(elem, mapV, v); err != nil { 72 return err 73 } 74 } 75 } 76 } 77 // It secondly checks the tags of attributes. 78 tagMap := structs.TagMapName(pointer, structTagPriority, true) 79 for tagK, tagV := range tagMap { 80 if _, ok := doneMap[tagV]; ok { 81 continue 82 } 83 if v, ok := paramsMap[tagK]; ok { 84 doneMap[tagV] = true 85 if err := bindVarToStructAttr(elem, tagV, v); err != nil { 86 return err 87 } 88 } 89 } 90 // It finally do the converting with default rules. 91 attrMap := make(map[string]struct{}) 92 elemType := elem.Type() 93 for i := 0; i < elem.NumField(); i++ { 94 // Only do converting to public attributes. 95 if !strutils.IsLetterUpper(elemType.Field(i).Name[0]) { 96 continue 97 } 98 attrMap[elemType.Field(i).Name] = struct{}{} 99 } 100 for mapK, mapV := range paramsMap { 101 name := "" 102 for _, checkName := range []string{ 103 strutils.UcFirst(mapK), 104 strutils.ReplaceByMap(mapK, map[string]string{ 105 "_": "", 106 "-": "", 107 " ": "", 108 })} { 109 if _, ok := doneMap[checkName]; ok { 110 continue 111 } 112 if _, ok := tagMap[checkName]; ok { 113 continue 114 } 115 // Loop to find the matched attribute name. 116 for value, _ := range attrMap { 117 if strings.EqualFold(checkName, value) { 118 name = value 119 break 120 } 121 if strings.EqualFold(checkName, strings.Replace(value, "_", "", -1)) { 122 name = value 123 break 124 } 125 } 126 doneMap[checkName] = true 127 if name != "" { 128 break 129 } 130 } 131 // No matching, give up this attribute converting. 132 if name == "" { 133 continue 134 } 135 if err := bindVarToStructAttr(elem, name, mapV); err != nil { 136 return err 137 } 138 } 139 return nil 140 } 141 142 // StructDeep do Struct function recursively. 143 // See Struct. 144 func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { 145 if err := Struct(params, pointer, mapping...); err != nil { 146 return err 147 } else { 148 rv, ok := pointer.(reflect.Value) 149 if !ok { 150 rv = reflect.ValueOf(pointer) 151 } 152 kind := rv.Kind() 153 if kind == reflect.Ptr { 154 rv = rv.Elem() 155 kind = rv.Kind() 156 } 157 switch kind { 158 case reflect.Struct: 159 rt := rv.Type() 160 for i := 0; i < rv.NumField(); i++ { 161 // Only do converting to public attributes. 162 if !strutils.IsLetterUpper(rt.Field(i).Name[0]) { 163 continue 164 } 165 trv := rv.Field(i) 166 switch trv.Kind() { 167 case reflect.Struct: 168 if err := StructDeep(params, trv, mapping...); err != nil { 169 return err 170 } 171 } 172 } 173 } 174 } 175 return nil 176 } 177 178 // 将参数值绑定到对象指定名称的属性上 179 func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) { 180 structFieldValue := elem.FieldByName(name) 181 // 键名与对象属性匹配检测,map中如果有struct不存在的属性,那么不做处理,直接return 182 if !structFieldValue.IsValid() { 183 return nil 184 } 185 // CanSet的属性必须为公开属性(首字母大写) 186 if !structFieldValue.CanSet() { 187 return nil 188 } 189 // 必须将value转换为struct属性的数据类型,这里必须用到gconv包 190 defer func() { 191 // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 192 if recover() != nil { 193 err = bindVarToReflectValue(structFieldValue, value) 194 } 195 }() 196 structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) 197 return nil 198 } 199 200 // 将参数值绑定到对象指定索引位置的属性上 201 func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) { 202 structFieldValue := elem.FieldByIndex([]int{index}) 203 // 键名与对象属性匹配检测 204 if !structFieldValue.IsValid() { 205 return nil 206 } 207 // CanSet的属性必须为公开属性(首字母大写) 208 if !structFieldValue.CanSet() { 209 return nil 210 } 211 // 必须将value转换为struct属性的数据类型,这里必须用到gconv包 212 defer func() { 213 // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 214 if recover() != nil { 215 err = bindVarToReflectValue(structFieldValue, value) 216 } 217 }() 218 structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) 219 return nil 220 } 221 222 // 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换(处理复杂类型) 223 func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) error { 224 switch structFieldValue.Kind() { 225 // 属性为结构体 226 case reflect.Struct: 227 if err := Struct(value, structFieldValue); err != nil { 228 structFieldValue.Set(reflect.ValueOf(value)) 229 } 230 231 // 属性为数组类型 232 case reflect.Slice: 233 fallthrough 234 case reflect.Array: 235 a := reflect.Value{} 236 v := reflect.ValueOf(value) 237 if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { 238 if v.Len() > 0 { 239 a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len()) 240 t := a.Index(0).Type() 241 for i := 0; i < v.Len(); i++ { 242 if t.Kind() == reflect.Ptr { 243 e := reflect.New(t.Elem()).Elem() 244 if err := Struct(v.Index(i).Interface(), e); err != nil { 245 e.Set(reflect.ValueOf(v.Index(i).Interface())) 246 } 247 a.Index(i).Set(e.Addr()) 248 } else { 249 e := reflect.New(t).Elem() 250 if err := Struct(v.Index(i).Interface(), e); err != nil { 251 e.Set(reflect.ValueOf(v.Index(i).Interface())) 252 } 253 a.Index(i).Set(e) 254 } 255 } 256 } 257 } else { 258 a = reflect.MakeSlice(structFieldValue.Type(), 1, 1) 259 t := a.Index(0).Type() 260 if t.Kind() == reflect.Ptr { 261 e := reflect.New(t.Elem()).Elem() 262 if err := Struct(value, e); err != nil { 263 e.Set(reflect.ValueOf(value)) 264 } 265 a.Index(0).Set(e.Addr()) 266 } else { 267 e := reflect.New(t).Elem() 268 if err := Struct(value, e); err != nil { 269 e.Set(reflect.ValueOf(value)) 270 } 271 a.Index(0).Set(e) 272 } 273 } 274 structFieldValue.Set(a) 275 276 // 属性为指针类型 277 case reflect.Ptr: 278 e := reflect.New(structFieldValue.Type().Elem()).Elem() 279 if err := Struct(value, e); err != nil { 280 e.Set(reflect.ValueOf(value)) 281 } 282 structFieldValue.Set(e.Addr()) 283 284 default: 285 return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String())) 286 } 287 return nil 288 }