github.com/gogf/gf@v1.16.9/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/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 // Structs converts any slice to given struct slice. 17 // Also see Scan, Struct. 18 func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { 19 return Scan(params, pointer, mapping...) 20 } 21 22 // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the 23 // specified tags for `params` key-value items to struct attribute names mapping. 24 // The parameter `priorityTag` supports multiple tags that can be joined with char ','. 25 func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) { 26 return doStructs(params, pointer, nil, priorityTag) 27 } 28 29 // StructsDeep converts any slice to given struct slice recursively. 30 // Deprecated, use Structs instead. 31 func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { 32 var keyToAttributeNameMapping map[string]string 33 if len(mapping) > 0 { 34 keyToAttributeNameMapping = mapping[0] 35 } 36 return doStructs(params, pointer, keyToAttributeNameMapping, "") 37 } 38 39 // doStructs converts any slice to given struct slice. 40 // 41 // It automatically checks and converts json string to []map if `params` is string/[]byte. 42 // 43 // The parameter `pointer` should be type of pointer to slice of struct. 44 // Note that if `pointer` is a pointer to another pointer of type of slice of struct, 45 // it will create the struct/pointer internally. 46 func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { 47 if params == nil { 48 // If `params` is nil, no conversion. 49 return nil 50 } 51 if pointer == nil { 52 return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") 53 } 54 55 if doStructsByDirectReflectSet(params, pointer) { 56 return nil 57 } 58 59 defer func() { 60 // Catch the panic, especially the reflect operation panics. 61 if exception := recover(); exception != nil { 62 if e, ok := exception.(errorStack); ok { 63 err = e 64 } else { 65 err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) 66 } 67 } 68 }() 69 // If given `params` is JSON, it then uses json.Unmarshal doing the converting. 70 switch r := params.(type) { 71 case []byte: 72 if json.Valid(r) { 73 if rv, ok := pointer.(reflect.Value); ok { 74 if rv.Kind() == reflect.Ptr { 75 return json.UnmarshalUseNumber(r, rv.Interface()) 76 } 77 } else { 78 return json.UnmarshalUseNumber(r, pointer) 79 } 80 } 81 case string: 82 if paramsBytes := []byte(r); json.Valid(paramsBytes) { 83 if rv, ok := pointer.(reflect.Value); ok { 84 if rv.Kind() == reflect.Ptr { 85 return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) 86 } 87 } else { 88 return json.UnmarshalUseNumber(paramsBytes, pointer) 89 } 90 } 91 } 92 // Pointer type check. 93 pointerRv, ok := pointer.(reflect.Value) 94 if !ok { 95 pointerRv = reflect.ValueOf(pointer) 96 if kind := pointerRv.Kind(); kind != reflect.Ptr { 97 return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind) 98 } 99 } 100 // Converting `params` to map slice. 101 var ( 102 paramsList []interface{} 103 paramsRv = reflect.ValueOf(params) 104 paramsKind = paramsRv.Kind() 105 ) 106 for paramsKind == reflect.Ptr { 107 paramsRv = paramsRv.Elem() 108 paramsKind = paramsRv.Kind() 109 } 110 switch paramsKind { 111 case reflect.Slice, reflect.Array: 112 paramsList = make([]interface{}, paramsRv.Len()) 113 for i := 0; i < paramsRv.Len(); i++ { 114 paramsList[i] = paramsRv.Index(i) 115 } 116 default: 117 var paramsMaps = Maps(params) 118 paramsList = make([]interface{}, len(paramsMaps)) 119 for i := 0; i < len(paramsMaps); i++ { 120 paramsList[i] = paramsMaps[i] 121 } 122 } 123 // If `params` is an empty slice, no conversion. 124 if len(paramsList) == 0 { 125 return nil 126 } 127 var ( 128 reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList)) 129 itemType = reflectElemArray.Index(0).Type() 130 itemTypeKind = itemType.Kind() 131 pointerRvElem = pointerRv.Elem() 132 pointerRvLength = pointerRvElem.Len() 133 ) 134 if itemTypeKind == reflect.Ptr { 135 // Pointer element. 136 for i := 0; i < len(paramsList); i++ { 137 var tempReflectValue reflect.Value 138 if i < pointerRvLength { 139 // Might be nil. 140 tempReflectValue = pointerRvElem.Index(i).Elem() 141 } 142 if !tempReflectValue.IsValid() { 143 tempReflectValue = reflect.New(itemType.Elem()).Elem() 144 } 145 if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { 146 return err 147 } 148 reflectElemArray.Index(i).Set(tempReflectValue.Addr()) 149 } 150 } else { 151 // Struct element. 152 for i := 0; i < len(paramsList); i++ { 153 var tempReflectValue reflect.Value 154 if i < pointerRvLength { 155 tempReflectValue = pointerRvElem.Index(i) 156 } else { 157 tempReflectValue = reflect.New(itemType).Elem() 158 } 159 if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { 160 return err 161 } 162 reflectElemArray.Index(i).Set(tempReflectValue) 163 } 164 } 165 pointerRv.Elem().Set(reflectElemArray) 166 return nil 167 } 168 169 // doStructsByDirectReflectSet do the converting directly using reflect Set. 170 // It returns true if success, or else false. 171 func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) { 172 v1 := reflect.ValueOf(pointer) 173 v2 := reflect.ValueOf(params) 174 if v1.Kind() == reflect.Ptr { 175 if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() { 176 elem.Set(v2) 177 ok = true 178 } 179 } 180 return ok 181 }