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