github.com/gogf/gf/v2@v2.7.4/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 "reflect" 11 "strings" 12 13 "github.com/gogf/gf/v2/errors/gcode" 14 "github.com/gogf/gf/v2/errors/gerror" 15 "github.com/gogf/gf/v2/internal/empty" 16 "github.com/gogf/gf/v2/internal/json" 17 "github.com/gogf/gf/v2/internal/utils" 18 "github.com/gogf/gf/v2/util/gconv/internal/localinterface" 19 "github.com/gogf/gf/v2/util/gconv/internal/structcache" 20 ) 21 22 // Struct maps the params key-value pairs to the corresponding struct object's attributes. 23 // The third parameter `mapping` is unnecessary, indicating the mapping rules between the 24 // custom key name and the attribute name(case-sensitive). 25 // 26 // Note: 27 // 1. The `params` can be any type of map/struct, usually a map. 28 // 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object 29 // or struct pointer. 30 // 3. Only the public attributes of struct object can be mapped. 31 // 4. If `params` is a map, the key of the map `params` can be lowercase. 32 // It will automatically convert the first letter of the key to uppercase 33 // in mapping procedure to do the matching. 34 // It ignores the map key, if it does not match. 35 func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { 36 return Scan(params, pointer, paramKeyToAttrMap...) 37 } 38 39 // StructTag acts as Struct but also with support for priority tag feature, which retrieves the 40 // specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping. 41 // The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','. 42 func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) { 43 return doStruct(params, pointer, nil, priorityTag) 44 } 45 46 // doStruct is the core internal converting function for any data to struct. 47 func doStruct( 48 params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string, 49 ) (err error) { 50 if params == nil { 51 // If `params` is nil, no conversion. 52 return nil 53 } 54 if pointer == nil { 55 return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") 56 } 57 58 // JSON content converting. 59 ok, err := doConvertWithJsonCheck(params, pointer) 60 if err != nil { 61 return err 62 } 63 if ok { 64 return nil 65 } 66 67 defer func() { 68 // Catch the panic, especially the reflection operation panics. 69 if exception := recover(); exception != nil { 70 if v, ok := exception.(error); ok && gerror.HasStack(v) { 71 err = v 72 } else { 73 err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception) 74 } 75 } 76 }() 77 78 var ( 79 paramsReflectValue reflect.Value 80 paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value` 81 pointerReflectValue reflect.Value 82 pointerReflectKind reflect.Kind 83 pointerElemReflectValue reflect.Value // The reflection value to struct element. 84 ) 85 if v, ok := params.(reflect.Value); ok { 86 paramsReflectValue = v 87 } else { 88 paramsReflectValue = reflect.ValueOf(params) 89 } 90 paramsInterface = paramsReflectValue.Interface() 91 if v, ok := pointer.(reflect.Value); ok { 92 pointerReflectValue = v 93 pointerElemReflectValue = v 94 } else { 95 pointerReflectValue = reflect.ValueOf(pointer) 96 pointerReflectKind = pointerReflectValue.Kind() 97 if pointerReflectKind != reflect.Ptr { 98 return gerror.NewCodef( 99 gcode.CodeInvalidParameter, 100 "destination pointer should be type of '*struct', but got '%v'", 101 pointerReflectKind, 102 ) 103 } 104 // Using IsNil on reflect.Ptr variable is OK. 105 if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() { 106 return gerror.NewCode( 107 gcode.CodeInvalidParameter, 108 "destination pointer cannot be nil", 109 ) 110 } 111 pointerElemReflectValue = pointerReflectValue.Elem() 112 } 113 114 // If `params` and `pointer` are the same type, the do directly assignment. 115 // For performance enhancement purpose. 116 if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok { 117 return nil 118 } 119 120 // custom convert. 121 if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok { 122 return err 123 } 124 125 // Normal unmarshalling interfaces checks. 126 if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok { 127 return err 128 } 129 130 // It automatically creates struct object if necessary. 131 // For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User. 132 if pointerElemReflectValue.Kind() == reflect.Ptr { 133 if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() { 134 e := reflect.New(pointerElemReflectValue.Type().Elem()) 135 pointerElemReflectValue.Set(e) 136 defer func() { 137 if err != nil { 138 // If it is converted failed, it reset the `pointer` to nil. 139 pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem())) 140 } 141 }() 142 } 143 // if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok { 144 // return v.UnmarshalValue(params) 145 // } 146 // Note that it's `pointerElemReflectValue` here not `pointerReflectValue`. 147 if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok { 148 return err 149 } 150 // Retrieve its element, may be struct at last. 151 pointerElemReflectValue = pointerElemReflectValue.Elem() 152 } 153 paramsMap, ok := paramsInterface.(map[string]interface{}) 154 if !ok { 155 // paramsMap is the map[string]interface{} type variable for params. 156 // DO NOT use MapDeep here. 157 paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true) 158 if paramsMap == nil { 159 return gerror.NewCodef( 160 gcode.CodeInvalidParameter, 161 `convert params from "%#v" to "map[string]interface{}" failed`, 162 params, 163 ) 164 } 165 } 166 // Nothing to be done as the parameters are empty. 167 if len(paramsMap) == 0 { 168 return nil 169 } 170 // Get struct info from cache or parse struct and cache the struct info. 171 cachedStructInfo := structcache.GetCachedStructInfo( 172 pointerElemReflectValue.Type(), priorityTag, 173 ) 174 // Nothing to be converted. 175 if cachedStructInfo == nil { 176 return nil 177 } 178 // For the structure types of 0 tagOrFiledNameToFieldInfoMap, 179 // they also need to be cached to prevent invalid logic 180 if cachedStructInfo.HasNoFields() { 181 return nil 182 } 183 var ( 184 // Indicates that those values have been used and cannot be reused. 185 usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool() 186 cachedFieldInfo *structcache.CachedFieldInfo 187 paramsValue interface{} 188 ) 189 defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap) 190 191 // Firstly, search according to custom mapping rules. 192 // If a possible direct assignment is found, reduce the number of subsequent map searches. 193 for paramKey, fieldName := range paramKeyToAttrMap { 194 paramsValue, ok = paramsMap[paramKey] 195 if !ok { 196 continue 197 } 198 cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName) 199 if cachedFieldInfo != nil { 200 fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue) 201 if err = bindVarToStructField( 202 fieldValue, 203 paramsValue, 204 cachedFieldInfo, 205 paramKeyToAttrMap, 206 ); err != nil { 207 return err 208 } 209 if len(cachedFieldInfo.OtherSameNameField) > 0 { 210 if err = setOtherSameNameField( 211 cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap, 212 ); err != nil { 213 return err 214 } 215 } 216 usedParamsKeyOrTagNameMap[paramKey] = struct{}{} 217 } 218 } 219 // Already done converting for given `paramsMap`. 220 if len(usedParamsKeyOrTagNameMap) == len(paramsMap) { 221 return nil 222 } 223 // If the length of `paramsMap` is less than the number of fields, then loop based on `paramsMap` 224 if len(paramsMap) < len(cachedStructInfo.FieldConvertInfos) { 225 return bindStructWithLoopParamsMap( 226 paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo, 227 ) 228 } 229 return bindStructWithLoopFieldInfos( 230 paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo, 231 ) 232 } 233 234 func setOtherSameNameField( 235 cachedFieldInfo *structcache.CachedFieldInfo, 236 srcValue any, 237 structValue reflect.Value, 238 paramKeyToAttrMap map[string]string, 239 ) (err error) { 240 // loop the same field name of all sub attributes. 241 for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField { 242 fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes) 243 if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil { 244 return err 245 } 246 } 247 return nil 248 } 249 250 func bindStructWithLoopParamsMap( 251 paramsMap map[string]any, 252 structValue reflect.Value, 253 paramKeyToAttrMap map[string]string, 254 usedParamsKeyOrTagNameMap map[string]struct{}, 255 cachedStructInfo *structcache.CachedStructInfo, 256 ) (err error) { 257 var ( 258 fieldName string 259 cachedFieldInfo *structcache.CachedFieldInfo 260 fuzzLastKey string 261 fieldValue reflect.Value 262 paramKey string 263 paramValue any 264 ok bool 265 ) 266 for paramKey, paramValue = range paramsMap { 267 if _, ok = usedParamsKeyOrTagNameMap[paramKey]; ok { 268 continue 269 } 270 cachedFieldInfo = cachedStructInfo.GetFieldInfo(paramKey) 271 if cachedFieldInfo != nil { 272 fieldName = cachedFieldInfo.FieldName() 273 // already converted using its field name? 274 // the field name has the more priority than tag name. 275 _, ok = usedParamsKeyOrTagNameMap[fieldName] 276 if ok && cachedFieldInfo.IsField { 277 continue 278 } 279 fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) 280 if err = bindVarToStructField( 281 fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, 282 ); err != nil { 283 return err 284 } 285 // handle same field name in nested struct. 286 if len(cachedFieldInfo.OtherSameNameField) > 0 { 287 if err = setOtherSameNameField(cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap); err != nil { 288 return err 289 } 290 } 291 usedParamsKeyOrTagNameMap[fieldName] = struct{}{} 292 continue 293 } 294 295 // fuzzy matching. 296 for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos { 297 fieldName = cachedFieldInfo.FieldName() 298 if _, ok = usedParamsKeyOrTagNameMap[fieldName]; ok { 299 continue 300 } 301 fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string) 302 paramValue, ok = paramsMap[fuzzLastKey] 303 if !ok { 304 if strings.EqualFold( 305 cachedFieldInfo.RemoveSymbolsFieldName, utils.RemoveSymbols(paramKey), 306 ) { 307 paramValue, ok = paramsMap[paramKey] 308 // If it is found this time, update it based on what was not found last time. 309 cachedFieldInfo.LastFuzzyKey.Store(paramKey) 310 } 311 } 312 if ok { 313 fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) 314 if paramValue != nil { 315 if err = bindVarToStructField( 316 fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, 317 ); err != nil { 318 return err 319 } 320 // handle same field name in nested struct. 321 if len(cachedFieldInfo.OtherSameNameField) > 0 { 322 if err = setOtherSameNameField( 323 cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, 324 ); err != nil { 325 return err 326 } 327 } 328 } 329 usedParamsKeyOrTagNameMap[cachedFieldInfo.FieldName()] = struct{}{} 330 break 331 } 332 } 333 } 334 return nil 335 } 336 337 func bindStructWithLoopFieldInfos( 338 paramsMap map[string]any, 339 structValue reflect.Value, 340 paramKeyToAttrMap map[string]string, 341 usedParamsKeyOrTagNameMap map[string]struct{}, 342 cachedStructInfo *structcache.CachedStructInfo, 343 ) (err error) { 344 var ( 345 cachedFieldInfo *structcache.CachedFieldInfo 346 fuzzLastKey string 347 fieldValue reflect.Value 348 paramKey string 349 paramValue any 350 matched bool 351 ok bool 352 ) 353 for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos { 354 for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName { 355 if paramValue, ok = paramsMap[fieldTag]; !ok { 356 continue 357 } 358 if _, ok = usedParamsKeyOrTagNameMap[fieldTag]; ok { 359 matched = true 360 break 361 } 362 fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) 363 if err = bindVarToStructField( 364 fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, 365 ); err != nil { 366 return err 367 } 368 // handle same field name in nested struct. 369 if len(cachedFieldInfo.OtherSameNameField) > 0 { 370 if err = setOtherSameNameField( 371 cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, 372 ); err != nil { 373 return err 374 } 375 } 376 usedParamsKeyOrTagNameMap[fieldTag] = struct{}{} 377 matched = true 378 break 379 } 380 if matched { 381 matched = false 382 continue 383 } 384 385 fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string) 386 if paramValue, ok = paramsMap[fuzzLastKey]; !ok { 387 paramKey, paramValue = fuzzyMatchingFieldName( 388 cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap, 389 ) 390 ok = paramKey != "" 391 cachedFieldInfo.LastFuzzyKey.Store(paramKey) 392 } 393 if ok { 394 fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) 395 if paramValue != nil { 396 if err = bindVarToStructField( 397 fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, 398 ); err != nil { 399 return err 400 } 401 // handle same field name in nested struct. 402 if len(cachedFieldInfo.OtherSameNameField) > 0 { 403 if err = setOtherSameNameField( 404 cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, 405 ); err != nil { 406 return err 407 } 408 } 409 } 410 usedParamsKeyOrTagNameMap[paramKey] = struct{}{} 411 } 412 } 413 return nil 414 } 415 416 // fuzzy matching rule: 417 // to match field name and param key in case-insensitive and without symbols. 418 func fuzzyMatchingFieldName( 419 fieldName string, 420 paramsMap map[string]any, 421 usedParamsKeyMap map[string]struct{}, 422 ) (string, any) { 423 for paramKey, paramValue := range paramsMap { 424 if _, ok := usedParamsKeyMap[paramKey]; ok { 425 continue 426 } 427 removeParamKeyUnderline := utils.RemoveSymbols(paramKey) 428 if strings.EqualFold(fieldName, removeParamKeyUnderline) { 429 return paramKey, paramValue 430 } 431 } 432 return "", nil 433 } 434 435 // bindVarToStructField sets value to struct object attribute by name. 436 func bindVarToStructField( 437 fieldValue reflect.Value, 438 srcValue interface{}, 439 cachedFieldInfo *structcache.CachedFieldInfo, 440 paramKeyToAttrMap map[string]string, 441 ) (err error) { 442 if !fieldValue.IsValid() { 443 return nil 444 } 445 // CanSet checks whether attribute is public accessible. 446 if !fieldValue.CanSet() { 447 return nil 448 } 449 defer func() { 450 if exception := recover(); exception != nil { 451 if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil { 452 err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName()) 453 } 454 } 455 }() 456 // Directly converting. 457 if empty.IsNil(srcValue) { 458 fieldValue.Set(reflect.Zero(fieldValue.Type())) 459 return nil 460 } 461 // Try to call custom converter. 462 // Issue: https://github.com/gogf/gf/issues/3099 463 var ( 464 customConverterInput reflect.Value 465 ok bool 466 ) 467 if cachedFieldInfo.IsCustomConvert { 468 if customConverterInput, ok = srcValue.(reflect.Value); !ok { 469 customConverterInput = reflect.ValueOf(srcValue) 470 } 471 if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil { 472 return 473 } 474 } 475 if cachedFieldInfo.IsCommonInterface { 476 if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil { 477 return 478 } 479 } 480 // Common types use fast assignment logic 481 if cachedFieldInfo.ConvertFunc != nil { 482 cachedFieldInfo.ConvertFunc(srcValue, fieldValue) 483 return nil 484 } 485 doConvertWithReflectValueSet(fieldValue, doConvertInput{ 486 FromValue: srcValue, 487 ToTypeName: cachedFieldInfo.StructField.Type.String(), 488 ReferValue: fieldValue, 489 }) 490 return nil 491 } 492 493 // bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks. 494 func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) { 495 var pointer interface{} 496 if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() { 497 reflectValueAddr := reflectValue.Addr() 498 if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() { 499 return false, nil 500 } 501 // Not a pointer, but can token address, that makes it can be unmarshalled. 502 pointer = reflectValue.Addr().Interface() 503 } else { 504 if reflectValue.IsNil() || !reflectValue.IsValid() { 505 return false, nil 506 } 507 pointer = reflectValue.Interface() 508 } 509 // UnmarshalValue. 510 if v, ok := pointer.(localinterface.IUnmarshalValue); ok { 511 return ok, v.UnmarshalValue(value) 512 } 513 // UnmarshalText. 514 if v, ok := pointer.(localinterface.IUnmarshalText); ok { 515 var valueBytes []byte 516 if b, ok := value.([]byte); ok { 517 valueBytes = b 518 } else if s, ok := value.(string); ok { 519 valueBytes = []byte(s) 520 } else if f, ok := value.(localinterface.IString); ok { 521 valueBytes = []byte(f.String()) 522 } 523 if len(valueBytes) > 0 { 524 return ok, v.UnmarshalText(valueBytes) 525 } 526 } 527 // UnmarshalJSON. 528 if v, ok := pointer.(localinterface.IUnmarshalJSON); ok { 529 var valueBytes []byte 530 if b, ok := value.([]byte); ok { 531 valueBytes = b 532 } else if s, ok := value.(string); ok { 533 valueBytes = []byte(s) 534 } else if f, ok := value.(localinterface.IString); ok { 535 valueBytes = []byte(f.String()) 536 } 537 538 if len(valueBytes) > 0 { 539 // If it is not a valid JSON string, it then adds char `"` on its both sides to make it is. 540 if !json.Valid(valueBytes) { 541 newValueBytes := make([]byte, len(valueBytes)+2) 542 newValueBytes[0] = '"' 543 newValueBytes[len(newValueBytes)-1] = '"' 544 copy(newValueBytes[1:], valueBytes) 545 valueBytes = newValueBytes 546 } 547 return ok, v.UnmarshalJSON(valueBytes) 548 } 549 } 550 if v, ok := pointer.(localinterface.ISet); ok { 551 v.Set(value) 552 return ok, nil 553 } 554 return false, nil 555 } 556 557 // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`. 558 func bindVarToReflectValue( 559 structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string, 560 ) (err error) { 561 // JSON content converting. 562 ok, err := doConvertWithJsonCheck(value, structFieldValue) 563 if err != nil { 564 return err 565 } 566 if ok { 567 return nil 568 } 569 570 kind := structFieldValue.Kind() 571 // Converting using `Set` interface implements, for some types. 572 switch kind { 573 case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface: 574 if !structFieldValue.IsNil() { 575 if v, ok := structFieldValue.Interface().(localinterface.ISet); ok { 576 v.Set(value) 577 return nil 578 } 579 } 580 } 581 582 // Converting using reflection by kind. 583 switch kind { 584 case reflect.Map: 585 return doMapToMap(value, structFieldValue, paramKeyToAttrMap) 586 587 case reflect.Struct: 588 // Recursively converting for struct attribute. 589 if err = doStruct(value, structFieldValue, nil, ""); err != nil { 590 // Note there's reflect conversion mechanism here. 591 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 592 } 593 594 // Note that the slice element might be type of struct, 595 // so it uses Struct function doing the converting internally. 596 case reflect.Slice, reflect.Array: 597 var ( 598 reflectArray reflect.Value 599 reflectValue = reflect.ValueOf(value) 600 ) 601 if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array { 602 reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len()) 603 if reflectValue.Len() > 0 { 604 var ( 605 elemType = reflectArray.Index(0).Type() 606 elemTypeName string 607 converted bool 608 ) 609 for i := 0; i < reflectValue.Len(); i++ { 610 converted = false 611 elemTypeName = elemType.Name() 612 if elemTypeName == "" { 613 elemTypeName = elemType.String() 614 } 615 var elem reflect.Value 616 if elemType.Kind() == reflect.Ptr { 617 elem = reflect.New(elemType.Elem()).Elem() 618 } else { 619 elem = reflect.New(elemType).Elem() 620 } 621 if elem.Kind() == reflect.Struct { 622 if err = doStruct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil { 623 converted = true 624 } 625 } 626 if !converted { 627 doConvertWithReflectValueSet(elem, doConvertInput{ 628 FromValue: reflectValue.Index(i).Interface(), 629 ToTypeName: elemTypeName, 630 ReferValue: elem, 631 }) 632 } 633 if elemType.Kind() == reflect.Ptr { 634 // Before it sets the `elem` to array, do pointer converting if necessary. 635 elem = elem.Addr() 636 } 637 reflectArray.Index(i).Set(elem) 638 } 639 } 640 } else { 641 var ( 642 elem reflect.Value 643 elemType = structFieldValue.Type().Elem() 644 elemTypeName = elemType.Name() 645 converted bool 646 ) 647 switch reflectValue.Kind() { 648 case reflect.String: 649 // Value is empty string. 650 if reflectValue.IsZero() { 651 var elemKind = elemType.Kind() 652 // Try to find the original type kind of the slice element. 653 if elemKind == reflect.Ptr { 654 elemKind = elemType.Elem().Kind() 655 } 656 switch elemKind { 657 case reflect.String: 658 // Empty string cannot be assigned to string slice. 659 return nil 660 } 661 } 662 } 663 if elemTypeName == "" { 664 elemTypeName = elemType.String() 665 } 666 if elemType.Kind() == reflect.Ptr { 667 elem = reflect.New(elemType.Elem()).Elem() 668 } else { 669 elem = reflect.New(elemType).Elem() 670 } 671 if elem.Kind() == reflect.Struct { 672 if err = doStruct(value, elem, nil, ""); err == nil { 673 converted = true 674 } 675 } 676 if !converted { 677 doConvertWithReflectValueSet(elem, doConvertInput{ 678 FromValue: value, 679 ToTypeName: elemTypeName, 680 ReferValue: elem, 681 }) 682 } 683 if elemType.Kind() == reflect.Ptr { 684 // Before it sets the `elem` to array, do pointer converting if necessary. 685 elem = elem.Addr() 686 } 687 reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1) 688 reflectArray.Index(0).Set(elem) 689 } 690 structFieldValue.Set(reflectArray) 691 692 case reflect.Ptr: 693 if structFieldValue.IsNil() || structFieldValue.IsZero() { 694 // Nil or empty pointer, it creates a new one. 695 item := reflect.New(structFieldValue.Type().Elem()) 696 if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok { 697 structFieldValue.Set(item) 698 return err 699 } 700 elem := item.Elem() 701 if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil { 702 structFieldValue.Set(elem.Addr()) 703 } 704 } else { 705 // Not empty pointer, it assigns values to it. 706 return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap) 707 } 708 709 // It mainly and specially handles the interface of nil value. 710 case reflect.Interface: 711 if value == nil { 712 // Specially. 713 structFieldValue.Set(reflect.ValueOf((*interface{})(nil))) 714 } else { 715 // Note there's reflect conversion mechanism here. 716 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 717 } 718 719 default: 720 defer func() { 721 if exception := recover(); exception != nil { 722 err = gerror.NewCodef( 723 gcode.CodeInternalPanic, 724 `cannot convert value "%+v" to type "%s":%+v`, 725 value, 726 structFieldValue.Type().String(), 727 exception, 728 ) 729 } 730 }() 731 // It here uses reflect converting `value` to type of the attribute and assigns 732 // the result value to the attribute. It might fail and panic if the usual Go 733 // conversion rules do not allow conversion. 734 structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) 735 } 736 return nil 737 }