github.com/gogf/gf@v1.16.9/util/gconv/gconv_struct.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/empty" 13 "github.com/gogf/gf/internal/json" 14 "github.com/gogf/gf/internal/structs" 15 "reflect" 16 "strings" 17 18 "github.com/gogf/gf/internal/utils" 19 ) 20 21 // Struct maps the params key-value pairs to the corresponding struct object's attributes. 22 // The third parameter `mapping` is unnecessary, indicating the mapping rules between the 23 // custom key name and the attribute name(case sensitive). 24 // 25 // Note: 26 // 1. The `params` can be any type of map/struct, usually a map. 27 // 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object 28 // or struct pointer. 29 // 3. Only the public attributes of struct object can be mapped. 30 // 4. If `params` is a map, the key of the map `params` can be lowercase. 31 // It will automatically convert the first letter of the key to uppercase 32 // in mapping procedure to do the matching. 33 // It ignores the map key, if it does not match. 34 func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { 35 return Scan(params, pointer, mapping...) 36 } 37 38 // StructTag acts as Struct but also with support for priority tag feature, which retrieves the 39 // specified tags for `params` key-value items to struct attribute names mapping. 40 // The parameter `priorityTag` supports multiple tags that can be joined with char ','. 41 func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) { 42 return doStruct(params, pointer, nil, priorityTag) 43 } 44 45 // StructDeep do Struct function recursively. 46 // Deprecated, use Struct instead. 47 func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { 48 var keyToAttributeNameMapping map[string]string 49 if len(mapping) > 0 { 50 keyToAttributeNameMapping = mapping[0] 51 } 52 return doStruct(params, pointer, keyToAttributeNameMapping, "") 53 } 54 55 // doStruct is the core internal converting function for any data to struct. 56 func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { 57 if params == nil { 58 // If `params` is nil, no conversion. 59 return nil 60 } 61 if pointer == nil { 62 return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") 63 } 64 65 defer func() { 66 // Catch the panic, especially the reflect operation panics. 67 if exception := recover(); exception != nil { 68 if e, ok := exception.(errorStack); ok { 69 err = e 70 } else { 71 err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) 72 } 73 } 74 }() 75 76 // If given `params` is JSON, it then uses json.Unmarshal doing the converting. 77 switch r := params.(type) { 78 case []byte: 79 if json.Valid(r) { 80 if rv, ok := pointer.(reflect.Value); ok { 81 if rv.Kind() == reflect.Ptr { 82 return json.UnmarshalUseNumber(r, rv.Interface()) 83 } else if rv.CanAddr() { 84 return json.UnmarshalUseNumber(r, rv.Addr().Interface()) 85 } 86 } else { 87 return json.UnmarshalUseNumber(r, pointer) 88 } 89 } 90 case string: 91 if paramsBytes := []byte(r); json.Valid(paramsBytes) { 92 if rv, ok := pointer.(reflect.Value); ok { 93 if rv.Kind() == reflect.Ptr { 94 return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) 95 } else if rv.CanAddr() { 96 return json.UnmarshalUseNumber(paramsBytes, rv.Addr().Interface()) 97 } 98 } else { 99 return json.UnmarshalUseNumber(paramsBytes, pointer) 100 } 101 } 102 } 103 104 var ( 105 paramsReflectValue reflect.Value 106 paramsInterface interface{} // DO NOT use `params` directly as it might be type of `reflect.Value` 107 pointerReflectValue reflect.Value 108 pointerReflectKind reflect.Kind 109 pointerElemReflectValue reflect.Value // The pointed element. 110 ) 111 if v, ok := params.(reflect.Value); ok { 112 paramsReflectValue = v 113 } else { 114 paramsReflectValue = reflect.ValueOf(params) 115 } 116 paramsInterface = paramsReflectValue.Interface() 117 if v, ok := pointer.(reflect.Value); ok { 118 pointerReflectValue = v 119 pointerElemReflectValue = v 120 } else { 121 pointerReflectValue = reflect.ValueOf(pointer) 122 pointerReflectKind = pointerReflectValue.Kind() 123 if pointerReflectKind != reflect.Ptr { 124 return gerror.NewCodef(gcode.CodeInvalidParameter, "object pointer should be type of '*struct', but got '%v'", pointerReflectKind) 125 } 126 // Using IsNil on reflect.Ptr variable is OK. 127 if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() { 128 return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") 129 } 130 pointerElemReflectValue = pointerReflectValue.Elem() 131 } 132 // If `params` and `pointer` are the same type, the do directly assignment. 133 // For performance enhancement purpose. 134 if pointerElemReflectValue.IsValid() && pointerElemReflectValue.Type() == paramsReflectValue.Type() { 135 pointerElemReflectValue.Set(paramsReflectValue) 136 return nil 137 } 138 139 // Normal unmarshalling interfaces checks. 140 if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok { 141 return err 142 } 143 144 // It automatically creates struct object if necessary. 145 // For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User. 146 if pointerElemReflectValue.Kind() == reflect.Ptr { 147 if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() { 148 e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem() 149 pointerElemReflectValue.Set(e.Addr()) 150 } 151 //if v, ok := pointerElemReflectValue.Interface().(apiUnmarshalValue); ok { 152 // return v.UnmarshalValue(params) 153 //} 154 // Note that it's `pointerElemReflectValue` here not `pointerReflectValue`. 155 if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok { 156 return err 157 } 158 // Retrieve its element, may be struct at last. 159 pointerElemReflectValue = pointerElemReflectValue.Elem() 160 } 161 162 // paramsMap is the map[string]interface{} type variable for params. 163 // DO NOT use MapDeep here. 164 paramsMap := Map(paramsInterface) 165 if paramsMap == nil { 166 return gerror.NewCodef(gcode.CodeInvalidParameter, "convert params to map failed: %v", params) 167 } 168 169 // It only performs one converting to the same attribute. 170 // doneMap is used to check repeated converting, its key is the real attribute name 171 // of the struct. 172 doneMap := make(map[string]struct{}) 173 174 // The key of the attrMap is the attribute name of the struct, 175 // and the value is its replaced name for later comparison to improve performance. 176 var ( 177 tempName string 178 elemFieldType reflect.StructField 179 elemFieldValue reflect.Value 180 elemType = pointerElemReflectValue.Type() 181 attrMap = make(map[string]string) 182 ) 183 for i := 0; i < pointerElemReflectValue.NumField(); i++ { 184 elemFieldType = elemType.Field(i) 185 // Only do converting to public attributes. 186 if !utils.IsLetterUpper(elemFieldType.Name[0]) { 187 continue 188 } 189 // Maybe it's struct/*struct embedded. 190 if elemFieldType.Anonymous { 191 elemFieldValue = pointerElemReflectValue.Field(i) 192 // Ignore the interface attribute if it's nil. 193 if elemFieldValue.Kind() == reflect.Interface { 194 elemFieldValue = elemFieldValue.Elem() 195 if !elemFieldValue.IsValid() { 196 continue 197 } 198 } 199 if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil { 200 return err 201 } 202 } else { 203 tempName = elemFieldType.Name 204 attrMap[tempName] = utils.RemoveSymbols(tempName) 205 } 206 } 207 if len(attrMap) == 0 { 208 return nil 209 } 210 211 // The key of the tagMap is the attribute name of the struct, 212 // and the value is its replaced tag name for later comparison to improve performance. 213 var ( 214 tagMap = make(map[string]string) 215 priorityTagArray []string 216 ) 217 if priorityTag != "" { 218 priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), StructTagPriority...) 219 } else { 220 priorityTagArray = StructTagPriority 221 } 222 tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, priorityTagArray) 223 if err != nil { 224 return err 225 } 226 for tagName, attributeName := range tagToNameMap { 227 // If there's something else in the tag string, 228 // it uses the first part which is split using char ','. 229 // Eg: 230 // orm:"id, priority" 231 // orm:"name, with:uid=id" 232 tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0]) 233 } 234 235 var ( 236 attrName string 237 checkName string 238 ) 239 for mapK, mapV := range paramsMap { 240 attrName = "" 241 // It firstly checks the passed mapping rules. 242 if len(mapping) > 0 { 243 if passedAttrKey, ok := mapping[mapK]; ok { 244 attrName = passedAttrKey 245 } 246 } 247 // It secondly checks the predefined tags and matching rules. 248 if attrName == "" { 249 checkName = utils.RemoveSymbols(mapK) 250 // Loop to find the matched attribute name with or without 251 // string cases and chars like '-'/'_'/'.'/' '. 252 253 // Matching the parameters to struct tag names. 254 // The `tagV` is the attribute name of the struct. 255 for attrKey, cmpKey := range tagMap { 256 if strings.EqualFold(checkName, cmpKey) { 257 attrName = attrKey 258 break 259 } 260 } 261 // Matching the parameters to struct attributes. 262 if attrName == "" { 263 for attrKey, cmpKey := range attrMap { 264 // Eg: 265 // UserName eq user_name 266 // User-Name eq username 267 // username eq userName 268 // etc. 269 if strings.EqualFold(checkName, cmpKey) { 270 attrName = attrKey 271 break 272 } 273 } 274 } 275 } 276 277 // No matching, it gives up this attribute converting. 278 if attrName == "" { 279 continue 280 } 281 // If the attribute name is already checked converting, then skip it. 282 if _, ok := doneMap[attrName]; ok { 283 continue 284 } 285 // Mark it done. 286 doneMap[attrName] = struct{}{} 287 if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping, priorityTag); err != nil { 288 return err 289 } 290 } 291 return nil 292 } 293 294 // bindVarToStructAttr sets value to struct object attribute by name. 295 func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string, priorityTag string) (err error) { 296 structFieldValue := elem.FieldByName(name) 297 if !structFieldValue.IsValid() { 298 return nil 299 } 300 // CanSet checks whether attribute is public accessible. 301 if !structFieldValue.CanSet() { 302 return nil 303 } 304 defer func() { 305 if exception := recover(); exception != nil { 306 if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil { 307 err = gerror.WrapCodef(gcode.CodeInternalError, err, `error binding value to attribute "%s"`, name) 308 } 309 } 310 }() 311 // Directly converting. 312 if empty.IsNil(value) { 313 structFieldValue.Set(reflect.Zero(structFieldValue.Type())) 314 } else { 315 structFieldValue.Set(reflect.ValueOf(doConvert( 316 doConvertInput{ 317 FromValue: value, 318 ToTypeName: structFieldValue.Type().String(), 319 ReferValue: structFieldValue, 320 }, 321 ))) 322 } 323 return nil 324 } 325 326 // bindVarToReflectValueWithInterfaceCheck does binding using common interfaces checks. 327 func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (err error, ok bool) { 328 var pointer interface{} 329 if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() { 330 reflectValueAddr := reflectValue.Addr() 331 if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() { 332 return nil, false 333 } 334 // Not a pointer, but can token address, that makes it can be unmarshalled. 335 pointer = reflectValue.Addr().Interface() 336 } else { 337 if reflectValue.IsNil() || !reflectValue.IsValid() { 338 return nil, false 339 } 340 pointer = reflectValue.Interface() 341 } 342 // UnmarshalValue. 343 if v, ok := pointer.(apiUnmarshalValue); ok { 344 return v.UnmarshalValue(value), ok 345 } 346 // UnmarshalText. 347 if v, ok := pointer.(apiUnmarshalText); ok { 348 var valueBytes []byte 349 if b, ok := value.([]byte); ok { 350 valueBytes = b 351 } else if s, ok := value.(string); ok { 352 valueBytes = []byte(s) 353 } 354 if len(valueBytes) > 0 { 355 return v.UnmarshalText(valueBytes), ok 356 } 357 } 358 // UnmarshalJSON. 359 if v, ok := pointer.(apiUnmarshalJSON); ok { 360 var valueBytes []byte 361 if b, ok := value.([]byte); ok { 362 valueBytes = b 363 } else if s, ok := value.(string); ok { 364 valueBytes = []byte(s) 365 } 366 367 if len(valueBytes) > 0 { 368 // If it is not a valid JSON string, it then adds char `"` on its both sides to make it is. 369 if !json.Valid(valueBytes) { 370 newValueBytes := make([]byte, len(valueBytes)+2) 371 newValueBytes[0] = '"' 372 newValueBytes[len(newValueBytes)-1] = '"' 373 copy(newValueBytes[1:], valueBytes) 374 valueBytes = newValueBytes 375 } 376 return v.UnmarshalJSON(valueBytes), ok 377 } 378 } 379 if v, ok := pointer.(apiSet); ok { 380 v.Set(value) 381 return nil, ok 382 } 383 return nil, false 384 } 385 386 // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`. 387 func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) { 388 if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok { 389 return err 390 } 391 kind := structFieldValue.Kind() 392 // Converting using interface, for some kinds. 393 switch kind { 394 case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface: 395 if !structFieldValue.IsNil() { 396 if v, ok := structFieldValue.Interface().(apiSet); ok { 397 v.Set(value) 398 return nil 399 } 400 } 401 } 402 403 // Converting by kind. 404 switch kind { 405 case reflect.Map: 406 return doMapToMap(value, structFieldValue, mapping) 407 408 case reflect.Struct: 409 // Recursively converting for struct attribute. 410 if err := doStruct(value, structFieldValue, nil, ""); err != nil { 411 // Note there's reflect conversion mechanism here. 412 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 413 } 414 415 // Note that the slice element might be type of struct, 416 // so it uses Struct function doing the converting internally. 417 case reflect.Slice, reflect.Array: 418 a := reflect.Value{} 419 v := reflect.ValueOf(value) 420 if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { 421 a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len()) 422 if v.Len() > 0 { 423 t := a.Index(0).Type() 424 for i := 0; i < v.Len(); i++ { 425 if t.Kind() == reflect.Ptr { 426 e := reflect.New(t.Elem()).Elem() 427 if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil { 428 // Note there's reflect conversion mechanism here. 429 e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t)) 430 } 431 a.Index(i).Set(e.Addr()) 432 } else { 433 e := reflect.New(t).Elem() 434 if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil { 435 // Note there's reflect conversion mechanism here. 436 e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t)) 437 } 438 a.Index(i).Set(e) 439 } 440 } 441 } 442 } else { 443 a = reflect.MakeSlice(structFieldValue.Type(), 1, 1) 444 t := a.Index(0).Type() 445 if t.Kind() == reflect.Ptr { 446 e := reflect.New(t.Elem()).Elem() 447 if err := doStruct(value, e, nil, ""); err != nil { 448 // Note there's reflect conversion mechanism here. 449 e.Set(reflect.ValueOf(value).Convert(t)) 450 } 451 a.Index(0).Set(e.Addr()) 452 } else { 453 e := reflect.New(t).Elem() 454 if err := doStruct(value, e, nil, ""); err != nil { 455 // Note there's reflect conversion mechanism here. 456 e.Set(reflect.ValueOf(value).Convert(t)) 457 } 458 a.Index(0).Set(e) 459 } 460 } 461 structFieldValue.Set(a) 462 463 case reflect.Ptr: 464 item := reflect.New(structFieldValue.Type().Elem()) 465 if err, ok := bindVarToReflectValueWithInterfaceCheck(item, value); ok { 466 structFieldValue.Set(item) 467 return err 468 } 469 elem := item.Elem() 470 if err = bindVarToReflectValue(elem, value, mapping, priorityTag); err == nil { 471 structFieldValue.Set(elem.Addr()) 472 } 473 474 // It mainly and specially handles the interface of nil value. 475 case reflect.Interface: 476 if value == nil { 477 // Specially. 478 structFieldValue.Set(reflect.ValueOf((*interface{})(nil))) 479 } else { 480 // Note there's reflect conversion mechanism here. 481 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 482 } 483 484 default: 485 defer func() { 486 if exception := recover(); exception != nil { 487 err = gerror.NewCodef( 488 gcode.CodeInternalError, 489 `cannot convert value "%+v" to type "%s":%+v`, 490 value, 491 structFieldValue.Type().String(), 492 exception, 493 ) 494 } 495 }() 496 // It here uses reflect converting `value` to type of the attribute and assigns 497 // the result value to the attribute. It might fail and panic if the usual Go 498 // conversion rules do not allow conversion. 499 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 500 } 501 return nil 502 }