github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_scan.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 "github.com/wangyougui/gf/v2/internal/json" 15 ) 16 17 // Scan automatically checks the type of `pointer` and converts `params` to `pointer`. 18 // It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. 19 // 20 // TODO change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`. 21 func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { 22 if srcValue == nil { 23 // If `srcValue` is nil, no conversion. 24 return nil 25 } 26 if dstPointer == nil { 27 return gerror.NewCode( 28 gcode.CodeInvalidParameter, 29 `destination pointer should not be nil`, 30 ) 31 } 32 33 // json converting check. 34 ok, err := doConvertWithJsonCheck(srcValue, dstPointer) 35 if err != nil { 36 return err 37 } 38 if ok { 39 return nil 40 } 41 42 var ( 43 dstPointerReflectType reflect.Type 44 dstPointerReflectValue reflect.Value 45 ) 46 if v, ok := dstPointer.(reflect.Value); ok { 47 dstPointerReflectValue = v 48 dstPointerReflectType = v.Type() 49 } else { 50 dstPointerReflectValue = reflect.ValueOf(dstPointer) 51 // do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero. 52 dstPointerReflectType = reflect.TypeOf(dstPointer) 53 } 54 55 // pointer kind validation. 56 var dstPointerReflectKind = dstPointerReflectType.Kind() 57 if dstPointerReflectKind != reflect.Ptr { 58 if dstPointerReflectValue.CanAddr() { 59 dstPointerReflectValue = dstPointerReflectValue.Addr() 60 dstPointerReflectType = dstPointerReflectValue.Type() 61 dstPointerReflectKind = dstPointerReflectType.Kind() 62 } else { 63 return gerror.NewCodef( 64 gcode.CodeInvalidParameter, 65 `destination pointer should be type of pointer, but got type: %v`, 66 dstPointerReflectType, 67 ) 68 } 69 } 70 // direct assignment checks! 71 var srcValueReflectValue reflect.Value 72 if v, ok := srcValue.(reflect.Value); ok { 73 srcValueReflectValue = v 74 } else { 75 srcValueReflectValue = reflect.ValueOf(srcValue) 76 } 77 // if `srcValue` and `dstPointer` are the same type, the do directly assignment. 78 // For performance enhancement purpose. 79 var dstPointerReflectValueElem = dstPointerReflectValue.Elem() 80 // if `srcValue` and `dstPointer` are the same type, the do directly assignment. 81 // for performance enhancement purpose. 82 if ok = doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok { 83 return nil 84 } 85 86 // do the converting. 87 var ( 88 dstPointerReflectTypeElem = dstPointerReflectType.Elem() 89 dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind() 90 keyToAttributeNameMapping map[string]string 91 ) 92 if len(paramKeyToAttrMap) > 0 { 93 keyToAttributeNameMapping = paramKeyToAttrMap[0] 94 } 95 switch dstPointerReflectTypeElemKind { 96 case reflect.Map: 97 return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...) 98 99 case reflect.Array, reflect.Slice: 100 var ( 101 sliceElem = dstPointerReflectTypeElem.Elem() 102 sliceElemKind = sliceElem.Kind() 103 ) 104 for sliceElemKind == reflect.Ptr { 105 sliceElem = sliceElem.Elem() 106 sliceElemKind = sliceElem.Kind() 107 } 108 if sliceElemKind == reflect.Map { 109 return doMapToMaps(srcValue, dstPointer, paramKeyToAttrMap...) 110 } 111 return doStructs(srcValue, dstPointer, keyToAttributeNameMapping, "") 112 113 default: 114 return doStruct(srcValue, dstPointer, keyToAttributeNameMapping, "") 115 } 116 } 117 118 func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) { 119 if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() { 120 return false 121 } 122 switch { 123 // Example: 124 // UploadFile => UploadFile 125 // []UploadFile => []UploadFile 126 // *UploadFile => *UploadFile 127 // *[]UploadFile => *[]UploadFile 128 // map => map 129 // []map => []map 130 // *[]map => *[]map 131 case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type(): 132 dstPointerReflectValueElem.Set(srcValueReflectValue) 133 return true 134 135 // Example: 136 // UploadFile => *UploadFile 137 // []UploadFile => *[]UploadFile 138 // map => *map 139 // []map => *[]map 140 case dstPointerReflectValueElem.Kind() == reflect.Ptr && 141 dstPointerReflectValueElem.Elem().IsValid() && 142 dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type(): 143 dstPointerReflectValueElem.Elem().Set(srcValueReflectValue) 144 return true 145 146 // Example: 147 // *UploadFile => UploadFile 148 // *[]UploadFile => []UploadFile 149 // *map => map 150 // *[]map => []map 151 case srcValueReflectValue.Kind() == reflect.Ptr && 152 srcValueReflectValue.Elem().IsValid() && 153 dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type(): 154 dstPointerReflectValueElem.Set(srcValueReflectValue.Elem()) 155 return true 156 157 default: 158 return false 159 } 160 } 161 162 // doConvertWithJsonCheck does json converting check. 163 // If given `params` is JSON, it then uses json.Unmarshal doing the converting. 164 func doConvertWithJsonCheck(srcValue interface{}, dstPointer interface{}) (ok bool, err error) { 165 switch valueResult := srcValue.(type) { 166 case []byte: 167 if json.Valid(valueResult) { 168 if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok { 169 if dstPointerReflectType.Kind() == reflect.Ptr { 170 if dstPointerReflectType.IsNil() { 171 return false, nil 172 } 173 return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Interface()) 174 } else if dstPointerReflectType.CanAddr() { 175 return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Addr().Interface()) 176 } 177 } else { 178 return true, json.UnmarshalUseNumber(valueResult, dstPointer) 179 } 180 } 181 182 case string: 183 if valueBytes := []byte(valueResult); json.Valid(valueBytes) { 184 if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok { 185 if dstPointerReflectType.Kind() == reflect.Ptr { 186 if dstPointerReflectType.IsNil() { 187 return false, nil 188 } 189 return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Interface()) 190 } else if dstPointerReflectType.CanAddr() { 191 return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Addr().Interface()) 192 } 193 } else { 194 return true, json.UnmarshalUseNumber(valueBytes, dstPointer) 195 } 196 } 197 198 default: 199 // The `params` might be struct that implements interface function Interface, eg: gvar.Var. 200 if v, ok := srcValue.(iInterface); ok { 201 return doConvertWithJsonCheck(v.Interface(), dstPointer) 202 } 203 } 204 return false, nil 205 }