github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_structs.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/wangyougui/gf. 6 7 package gconv 8 9 import ( 10 "reflect" 11 12 "github.com/wangyougui/gf/v2/errors/gcode" 13 "github.com/wangyougui/gf/v2/errors/gerror" 14 ) 15 16 // Structs converts any slice to given struct slice. 17 // Also see Scan, Struct. 18 func Structs(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { 19 return Scan(params, pointer, paramKeyToAttrMap...) 20 } 21 22 // SliceStruct is alias of Structs. 23 func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { 24 return Structs(params, pointer, mapping...) 25 } 26 27 // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the 28 // specified tags for `params` key-value items to struct attribute names mapping. 29 // The parameter `priorityTag` supports multiple tags that can be joined with char ','. 30 func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) { 31 return doStructs(params, pointer, nil, priorityTag) 32 } 33 34 // doStructs converts any slice to given struct slice. 35 // 36 // It automatically checks and converts json string to []map if `params` is string/[]byte. 37 // 38 // The parameter `pointer` should be type of pointer to slice of struct. 39 // Note that if `pointer` is a pointer to another pointer of type of slice of struct, 40 // it will create the struct/pointer internally. 41 func doStructs( 42 params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string, 43 ) (err error) { 44 defer func() { 45 // Catch the panic, especially the reflection operation panics. 46 if exception := recover(); exception != nil { 47 if v, ok := exception.(error); ok && gerror.HasStack(v) { 48 err = v 49 } else { 50 err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception) 51 } 52 } 53 }() 54 55 // Pointer type check. 56 pointerRv, ok := pointer.(reflect.Value) 57 if !ok { 58 pointerRv = reflect.ValueOf(pointer) 59 if kind := pointerRv.Kind(); kind != reflect.Ptr { 60 return gerror.NewCodef( 61 gcode.CodeInvalidParameter, 62 "pointer should be type of pointer, but got: %v", kind, 63 ) 64 } 65 } 66 // Converting `params` to map slice. 67 var ( 68 paramsList []interface{} 69 paramsRv = reflect.ValueOf(params) 70 paramsKind = paramsRv.Kind() 71 ) 72 for paramsKind == reflect.Ptr { 73 paramsRv = paramsRv.Elem() 74 paramsKind = paramsRv.Kind() 75 } 76 switch paramsKind { 77 case reflect.Slice, reflect.Array: 78 paramsList = make([]interface{}, paramsRv.Len()) 79 for i := 0; i < paramsRv.Len(); i++ { 80 paramsList[i] = paramsRv.Index(i).Interface() 81 } 82 default: 83 var paramsMaps = Maps(params) 84 paramsList = make([]interface{}, len(paramsMaps)) 85 for i := 0; i < len(paramsMaps); i++ { 86 paramsList[i] = paramsMaps[i] 87 } 88 } 89 // If `params` is an empty slice, no conversion. 90 if len(paramsList) == 0 { 91 return nil 92 } 93 var ( 94 reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList)) 95 itemType = reflectElemArray.Index(0).Type() 96 itemTypeKind = itemType.Kind() 97 pointerRvElem = pointerRv.Elem() 98 pointerRvLength = pointerRvElem.Len() 99 ) 100 if itemTypeKind == reflect.Ptr { 101 // Pointer element. 102 for i := 0; i < len(paramsList); i++ { 103 var tempReflectValue reflect.Value 104 if i < pointerRvLength { 105 // Might be nil. 106 tempReflectValue = pointerRvElem.Index(i).Elem() 107 } 108 if !tempReflectValue.IsValid() { 109 tempReflectValue = reflect.New(itemType.Elem()).Elem() 110 } 111 if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil { 112 return err 113 } 114 reflectElemArray.Index(i).Set(tempReflectValue.Addr()) 115 } 116 } else { 117 // Struct element. 118 for i := 0; i < len(paramsList); i++ { 119 var tempReflectValue reflect.Value 120 if i < pointerRvLength { 121 tempReflectValue = pointerRvElem.Index(i) 122 } else { 123 tempReflectValue = reflect.New(itemType).Elem() 124 } 125 if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil { 126 return err 127 } 128 reflectElemArray.Index(i).Set(tempReflectValue) 129 } 130 } 131 pointerRv.Elem().Set(reflectElemArray) 132 return nil 133 }