github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/base/fill/struct.go (about) 1 // Package fill assigns arbitrary values to struct fields using reflection. 2 // "fill" is case-insensitive, and obeys the "json" field tag if present. 3 // It's primary use is to support decoding data from a number of serialization 4 // formats (JSON,YAML,CBOR) into an intermediate map[string]interface{} value 5 // which can then be used to "fill" arbitrary struct values 6 package fill 7 8 import ( 9 "fmt" 10 "reflect" 11 "strings" 12 "time" 13 ) 14 15 // Struct fills in the values of an arbitrary structure using an already deserialized 16 // map of nested data. Fields names are case-insensitive. Unknown fields are treated as an 17 // error, *unless* the output structure implements the ArbitrarySetter interface. 18 func Struct(fields map[string]interface{}, output interface{}) error { 19 target := reflect.ValueOf(output) 20 if target.Kind() == reflect.Ptr { 21 target = target.Elem() 22 } 23 collector := NewErrorCollector() 24 putFieldsToTargetStruct(fields, target, collector) 25 return collector.AsSingleError() 26 } 27 28 // ArbitrarySetter should be implemented by structs that can store arbitrary fields in a private map. 29 type ArbitrarySetter interface { 30 SetArbitrary(string, interface{}) error 31 } 32 33 var ( 34 // timeObj and strObj are used for reflect.TypeOf 35 timeObj time.Time 36 strObj string 37 byteObj byte 38 ) 39 40 // putFieldsToTargetStruct iterates over the fields in the target struct, and assigns each 41 // field the value from the `fields` map. Recursively call this for an sub structures. Field 42 // names are treated as case-insensitive. Return any errors found during this process, or nil if 43 // there are no errors. 44 func putFieldsToTargetStruct(fields map[string]interface{}, target reflect.Value, collector *ErrorCollector) { 45 if target.Kind() != reflect.Struct { 46 collector.Add(fmt.Errorf("can only assign fields to a struct")) 47 return 48 } 49 50 // Collect real key names used by the `fields` map. 51 realKeys := make([]string, 0) 52 for k := range fields { 53 realKeys = append(realKeys, k) 54 } 55 // Handle case-insensitivity by building a map from lowercase keys to real keys. 56 caseMap := make(map[string]string) 57 for i := 0; i < len(realKeys); i++ { 58 realKey := realKeys[i] 59 lowerKey := strings.ToLower(realKey) 60 caseMap[lowerKey] = realKey 61 } 62 63 // Keep track of which keys have been used from the `fields` map 64 usedKeys := make(map[string]bool) 65 66 for i := 0; i < target.NumField(); i++ { 67 // Lowercase the key in order to make matching case-insensitive. 68 fieldName := target.Type().Field(i).Name 69 lowerName := strings.ToLower(fieldName) 70 fieldTag := target.Type().Field(i).Tag 71 if fieldTag != "" && fieldTag.Get("json") != "" { 72 jsonName := fieldTag.Get("json") 73 pos := strings.Index(jsonName, ",") 74 if pos != -1 { 75 jsonName = jsonName[:pos] 76 } 77 lowerName = strings.ToLower(jsonName) 78 } 79 80 val, ok := fields[caseMap[lowerName]] 81 if !ok { 82 // Nothing to assign to this field, go to next. 83 continue 84 } 85 usedKeys[caseMap[lowerName]] = true 86 if val == nil { 87 // Don't try and assign a nil value. 88 continue 89 } 90 91 collector.PushField(fieldName) 92 putValueToPlace(val, target.Field(i), collector) 93 collector.PopField() 94 } 95 96 // If the target struct is able, assign unknown keys to it. 97 arbitrarySetter := getArbitrarySetter(target) 98 99 // Iterate over keys in the `fields` data, see if there were any keys that were not stored in 100 // the target struct. 101 for i := 0; i < len(realKeys); i++ { 102 k := realKeys[i] 103 if _, ok := usedKeys[k]; !ok { 104 // If target struct allows storing unknown keys to a map of arbitrary data. 105 if arbitrarySetter != nil { 106 arbitrarySetter.SetArbitrary(k, fields[k]) 107 continue 108 } 109 // Otherwise, unknown fields are an error. 110 collector.Add(fmt.Errorf("at \"%s\": not found in struct %s", k, target.Type())) 111 } 112 } 113 } 114 115 // putValueToPlace stores the val at the place, recusively if necessary 116 func putValueToPlace(val interface{}, place reflect.Value, collector *ErrorCollector) { 117 switch place.Kind() { 118 case reflect.Struct: 119 // Specially handle time.Time, represented as a string, which needs to be parsed. 120 if place.Type() == reflect.TypeOf(timeObj) { 121 timeText, ok := val.(string) 122 if ok { 123 ts, err := time.Parse(time.RFC3339, timeText) 124 if err != nil { 125 err = fmt.Errorf("could not parse time: \"%s\"", timeText) 126 collector.Add(err) 127 } else { 128 place.Set(reflect.ValueOf(ts)) 129 } 130 return 131 } 132 err := &FieldError{Want: "time", Got: reflect.TypeOf(val).Name(), Val: val} 133 collector.Add(err) 134 return 135 } 136 // Struct must be assigned from a map. 137 component := toStringMap(val) 138 if component == nil { 139 collector.Add(fmt.Errorf("could not convert to map[string]")) 140 return 141 } 142 // Recursion to handle sub-component. 143 putFieldsToTargetStruct(component, place, collector) 144 case reflect.Map: 145 if val == nil { 146 // If map is nil, nothing more to do. 147 return 148 } 149 ms, ok := val.(map[string]interface{}) 150 if ok { 151 // Special case map[string]string, convert values to strings. 152 if place.Type().Elem() == reflect.TypeOf(strObj) { 153 strmap := make(map[string]string) 154 for k, v := range ms { 155 strmap[k] = fmt.Sprintf("%s", v) 156 } 157 place.Set(reflect.ValueOf(strmap)) 158 return 159 } 160 if place.CanSet() { 161 place.Set(reflect.ValueOf(ms)) 162 } 163 return 164 } 165 mi, ok := val.(map[interface{}]interface{}) 166 if ok { 167 // Special case map[string]string, convert values to strings. 168 if place.Type().Elem() == reflect.TypeOf(strObj) { 169 strmap := make(map[string]string) 170 for k, v := range mi { 171 strmap[fmt.Sprintf("%s", k)] = fmt.Sprintf("%s", v) 172 } 173 place.Set(reflect.ValueOf(strmap)) 174 return 175 } 176 if place.CanSet() { 177 place.Set(reflect.ValueOf(ensureMapsHaveStringKeys(mi))) 178 } 179 return 180 } 181 // Error due to not being able to convert. 182 collector.Add(&FieldError{Want: "map", Got: reflect.TypeOf(val).Name(), Val: val}) 183 return 184 case reflect.Slice: 185 if val == nil { 186 // If slice is nil, nothing more to do. 187 return 188 } 189 190 if place.Type().Elem() == reflect.TypeOf(byteObj) { 191 // Special behavior for raw binary data, either a byte array or a string. 192 // TODO(dlong): Look into if this is needed for reflect.Array. If yes, add 193 // functionality and tests, if no, document why not. 194 byteSlice, ok := val.([]byte) 195 if ok { 196 place.SetBytes(byteSlice) 197 return 198 } 199 text, ok := val.(string) 200 if ok { 201 place.SetBytes([]byte(text)) 202 return 203 } 204 collector.Add(fmt.Errorf("need type byte slice, value %v", val)) 205 return 206 } 207 208 slice, ok := val.([]interface{}) 209 if !ok { 210 collector.Add(fmt.Errorf("need type slice, value %v", val)) 211 return 212 } 213 // Get size of type of the slice to deserialize. 214 size := len(slice) 215 sliceType := place.Type().Elem() 216 // Construct a new, empty slice of the same size. 217 create := reflect.MakeSlice(reflect.SliceOf(sliceType), size, size) 218 // Fill in each element. 219 for i := 0; i < size; i++ { 220 elem := reflect.Indirect(reflect.New(sliceType)) 221 collector.PushField(fmt.Sprintf("%d", i)) 222 putValueToPlace(slice[i], elem, collector) 223 collector.PopField() 224 create.Index(i).Set(elem) 225 } 226 place.Set(create) 227 return 228 case reflect.Array: 229 if val == nil { 230 // If slice is nil, nothing more to do. 231 return 232 } 233 slice, ok := val.([]interface{}) 234 if !ok { 235 collector.Add(fmt.Errorf("need type array, value %s", val)) 236 return 237 } 238 // Get size of type of the slice to deserialize. 239 size := len(slice) 240 targetElem := place.Type().Elem() 241 targetSize := place.Type().Len() 242 if size != targetSize { 243 collector.Add(fmt.Errorf("need array of size %d, got size %d", targetSize, size)) 244 return 245 } 246 // Construct array of appropriate size and type. 247 arrayType := reflect.ArrayOf(targetSize, targetElem) 248 create := reflect.New(arrayType).Elem() 249 // Fill in each element. 250 for i := 0; i < size; i++ { 251 elem := reflect.Indirect(reflect.New(targetElem)) 252 putValueToPlace(slice[i], elem, collector) 253 create.Index(i).Set(elem) 254 } 255 place.Set(create) 256 return 257 case reflect.Ptr: 258 if val == nil { 259 // If pointer is nil, nothing more to do. 260 return 261 } 262 // Allocate a new pointer for the sub-component to be filled in. 263 alloc := reflect.New(place.Type().Elem()) 264 place.Set(alloc) 265 inner := alloc.Elem() 266 putValueToPlace(val, inner, collector) 267 return 268 default: 269 collector.Add(putValueToUnit(val, place)) 270 return 271 } 272 } 273 274 // putValueToUnit stores the val at the place, as long as it is a unitary (non-compound) type 275 func putValueToUnit(val interface{}, place reflect.Value) error { 276 switch place.Kind() { 277 case reflect.Int: 278 num, ok := val.(int) 279 if ok { 280 place.SetInt(int64(num)) 281 return nil 282 } 283 numFloat, ok := val.(float64) 284 if ok { 285 place.SetInt(int64(numFloat)) 286 return nil 287 } 288 return &FieldError{Want: "int", Got: reflect.TypeOf(val).Name(), Val: val} 289 case reflect.Int64: 290 num, ok := val.(int) 291 if ok { 292 place.SetInt(int64(num)) 293 return nil 294 } 295 num64, ok := val.(int64) 296 if ok { 297 place.SetInt(num64) 298 return nil 299 } 300 float64, ok := val.(float64) 301 if ok { 302 place.SetInt(int64(float64)) 303 return nil 304 } 305 return &FieldError{Want: "int64", Got: reflect.TypeOf(val).Name(), Val: val} 306 case reflect.Uint64: 307 num, ok := val.(uint) 308 if ok { 309 place.SetUint(uint64(num)) 310 return nil 311 } 312 num64, ok := val.(uint64) 313 if ok { 314 place.SetUint(num64) 315 return nil 316 } 317 float64, ok := val.(float64) 318 if ok { 319 place.SetUint(uint64(float64)) 320 return nil 321 } 322 return &FieldError{Want: "uint64", Got: reflect.TypeOf(val).Name(), Val: val} 323 case reflect.Float64: 324 num, ok := val.(int) 325 if ok { 326 place.SetFloat(float64(num)) 327 return nil 328 } 329 numFloat, ok := val.(float64) 330 if ok { 331 place.SetFloat(numFloat) 332 return nil 333 } 334 return &FieldError{Want: "float64", Got: reflect.TypeOf(val).Name(), Val: val} 335 case reflect.String: 336 text, ok := val.(string) 337 if ok { 338 place.SetString(text) 339 return nil 340 } 341 return &FieldError{Want: "string", Got: reflect.TypeOf(val).Name(), Val: val} 342 case reflect.Bool: 343 b, ok := val.(bool) 344 if ok { 345 place.SetBool(b) 346 return nil 347 } 348 return &FieldError{Want: "bool", Got: reflect.TypeOf(val).Name(), Val: val} 349 case reflect.Interface: 350 imap, ok := val.(map[interface{}]interface{}) 351 if ok { 352 place.Set(reflect.ValueOf(ensureMapsHaveStringKeys(imap))) 353 return nil 354 } 355 place.Set(reflect.ValueOf(val)) 356 return nil 357 default: 358 return fmt.Errorf("unknown kind %s", place.Kind()) 359 } 360 } 361 362 // toStringMap converts the input to a map[string] if able. This is needed because, while JSON 363 // correctly deserializes sub structures to map[string], YAML instead deserializes to 364 // map[interface{}]interface{}, so we need to manually convert this case to map[string]. 365 func toStringMap(obj interface{}) map[string]interface{} { 366 m, ok := obj.(map[string]interface{}) 367 if ok { 368 return m 369 } 370 imap, ok := obj.(map[interface{}]interface{}) 371 if ok { 372 return ensureMapsHaveStringKeys(imap) 373 } 374 return nil 375 } 376 377 // ensureMapsHaveStringKeys will recursively convert map's key to be strings. This will allow us 378 // to serialize back into JSON. 379 func ensureMapsHaveStringKeys(imap map[interface{}]interface{}) map[string]interface{} { 380 build := make(map[string]interface{}) 381 for k, v := range imap { 382 switch x := v.(type) { 383 case map[interface{}]interface{}: 384 v = ensureMapsHaveStringKeys(x) 385 case []interface{}: 386 for i, elem := range x { 387 if inner, ok := elem.(map[interface{}]interface{}); ok { 388 x[i] = ensureMapsHaveStringKeys(inner) 389 } 390 } 391 } 392 build[fmt.Sprintf("%s", k)] = v 393 } 394 return build 395 } 396 397 // getArbitrarySetter returns a ArbitrarySetter if the target implements it. 398 func getArbitrarySetter(target reflect.Value) ArbitrarySetter { 399 if !target.CanAddr() { 400 return nil 401 } 402 ptr := target.Addr() 403 iface := ptr.Interface() 404 if setter, ok := iface.(ArbitrarySetter); ok { 405 return setter 406 } 407 return nil 408 }