github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_map.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 "strings" 12 13 "github.com/wangyougui/gf/v2/internal/empty" 14 "github.com/wangyougui/gf/v2/internal/json" 15 "github.com/wangyougui/gf/v2/internal/utils" 16 "github.com/wangyougui/gf/v2/util/gtag" 17 ) 18 19 type recursiveType string 20 21 const ( 22 recursiveTypeAuto recursiveType = "auto" 23 recursiveTypeTrue recursiveType = "true" 24 ) 25 26 // MapOption specifies the option for map converting. 27 type MapOption struct { 28 // Deep marks doing Map function recursively, which means if the attribute of given converting value 29 // is also a struct/*struct, it automatically calls Map function on this attribute converting it to 30 // a map[string]interface{} type variable. 31 Deep bool 32 33 // OmitEmpty ignores the attributes that has json `omitempty` tag. 34 OmitEmpty bool 35 36 // Tags specifies the converted map key name by struct tag name. 37 Tags []string 38 } 39 40 // Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a 41 // map/struct/*struct type, then the conversion will fail and returns nil. 42 // 43 // If `value` is a struct/*struct object, the second parameter `tags` specifies the most priority 44 // tags that will be detected, otherwise it detects the tags in order of: 45 // gconv, json, field name. 46 func Map(value interface{}, option ...MapOption) map[string]interface{} { 47 return doMapConvert(value, recursiveTypeAuto, false, option...) 48 } 49 50 // MapDeep does Map function recursively, which means if the attribute of `value` 51 // is also a struct/*struct, calls Map function on this attribute converting it to 52 // a map[string]interface{} type variable. 53 // Deprecated: used Map instead. 54 func MapDeep(value interface{}, tags ...string) map[string]interface{} { 55 return doMapConvert(value, recursiveTypeTrue, false, MapOption{ 56 Deep: true, 57 Tags: tags, 58 }) 59 } 60 61 // doMapConvert implements the map converting. 62 // It automatically checks and converts json string to map if `value` is string/[]byte. 63 // 64 // TODO completely implement the recursive converting for all types, especially the map. 65 func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, option ...MapOption) map[string]interface{} { 66 if value == nil { 67 return nil 68 } 69 // It redirects to its underlying value if it has implemented interface iVal. 70 if v, ok := value.(iVal); ok { 71 value = v.Val() 72 } 73 74 var ( 75 usedOption = getUsedMapOption(option...) 76 newTags = gtag.StructTagPriority 77 ) 78 if usedOption.Deep { 79 recursive = recursiveTypeTrue 80 } 81 switch len(usedOption.Tags) { 82 case 0: 83 // No need handling. 84 case 1: 85 newTags = append(strings.Split(usedOption.Tags[0], ","), gtag.StructTagPriority...) 86 default: 87 newTags = append(usedOption.Tags, gtag.StructTagPriority...) 88 } 89 // Assert the common combination of types, and finally it uses reflection. 90 dataMap := make(map[string]interface{}) 91 switch r := value.(type) { 92 case string: 93 // If it is a JSON string, automatically unmarshal it! 94 if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' { 95 if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil { 96 return nil 97 } 98 } else { 99 return nil 100 } 101 case []byte: 102 // If it is a JSON string, automatically unmarshal it! 103 if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' { 104 if err := json.UnmarshalUseNumber(r, &dataMap); err != nil { 105 return nil 106 } 107 } else { 108 return nil 109 } 110 case map[interface{}]interface{}: 111 recursiveOption := usedOption 112 recursiveOption.Tags = newTags 113 for k, v := range r { 114 dataMap[String(k)] = doMapConvertForMapOrStructValue( 115 doMapConvertForMapOrStructValueInput{ 116 IsRoot: false, 117 Value: v, 118 RecursiveType: recursive, 119 RecursiveOption: recursive == recursiveTypeTrue, 120 Option: recursiveOption, 121 }, 122 ) 123 } 124 case map[interface{}]string: 125 for k, v := range r { 126 dataMap[String(k)] = v 127 } 128 case map[interface{}]int: 129 for k, v := range r { 130 dataMap[String(k)] = v 131 } 132 case map[interface{}]uint: 133 for k, v := range r { 134 dataMap[String(k)] = v 135 } 136 case map[interface{}]float32: 137 for k, v := range r { 138 dataMap[String(k)] = v 139 } 140 case map[interface{}]float64: 141 for k, v := range r { 142 dataMap[String(k)] = v 143 } 144 case map[string]bool: 145 for k, v := range r { 146 dataMap[k] = v 147 } 148 case map[string]int: 149 for k, v := range r { 150 dataMap[k] = v 151 } 152 case map[string]uint: 153 for k, v := range r { 154 dataMap[k] = v 155 } 156 case map[string]float32: 157 for k, v := range r { 158 dataMap[k] = v 159 } 160 case map[string]float64: 161 for k, v := range r { 162 dataMap[k] = v 163 } 164 case map[string]string: 165 for k, v := range r { 166 dataMap[k] = v 167 } 168 case map[string]interface{}: 169 if recursive == recursiveTypeTrue { 170 recursiveOption := usedOption 171 recursiveOption.Tags = newTags 172 // A copy of current map. 173 for k, v := range r { 174 dataMap[k] = doMapConvertForMapOrStructValue( 175 doMapConvertForMapOrStructValueInput{ 176 IsRoot: false, 177 Value: v, 178 RecursiveType: recursive, 179 RecursiveOption: recursive == recursiveTypeTrue, 180 Option: recursiveOption, 181 }, 182 ) 183 } 184 } else { 185 // It returns the map directly without any changing. 186 return r 187 } 188 case map[int]interface{}: 189 recursiveOption := usedOption 190 recursiveOption.Tags = newTags 191 for k, v := range r { 192 dataMap[String(k)] = doMapConvertForMapOrStructValue( 193 doMapConvertForMapOrStructValueInput{ 194 IsRoot: false, 195 Value: v, 196 RecursiveType: recursive, 197 RecursiveOption: recursive == recursiveTypeTrue, 198 Option: recursiveOption, 199 }, 200 ) 201 } 202 case map[int]string: 203 for k, v := range r { 204 dataMap[String(k)] = v 205 } 206 case map[uint]string: 207 for k, v := range r { 208 dataMap[String(k)] = v 209 } 210 211 default: 212 // Not a common type, it then uses reflection for conversion. 213 var reflectValue reflect.Value 214 if v, ok := value.(reflect.Value); ok { 215 reflectValue = v 216 } else { 217 reflectValue = reflect.ValueOf(value) 218 } 219 reflectKind := reflectValue.Kind() 220 // If it is a pointer, we should find its real data type. 221 for reflectKind == reflect.Ptr { 222 reflectValue = reflectValue.Elem() 223 reflectKind = reflectValue.Kind() 224 } 225 switch reflectKind { 226 // If `value` is type of array, it converts the value of even number index as its key and 227 // the value of odd number index as its corresponding value, for example: 228 // []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"} 229 // []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil} 230 case reflect.Slice, reflect.Array: 231 length := reflectValue.Len() 232 for i := 0; i < length; i += 2 { 233 if i+1 < length { 234 dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface() 235 } else { 236 dataMap[String(reflectValue.Index(i).Interface())] = nil 237 } 238 } 239 case reflect.Map, reflect.Struct, reflect.Interface: 240 recursiveOption := usedOption 241 recursiveOption.Tags = newTags 242 convertedValue := doMapConvertForMapOrStructValue( 243 doMapConvertForMapOrStructValueInput{ 244 IsRoot: true, 245 Value: value, 246 RecursiveType: recursive, 247 RecursiveOption: recursive == recursiveTypeTrue, 248 Option: recursiveOption, 249 MustMapReturn: mustMapReturn, 250 }, 251 ) 252 if m, ok := convertedValue.(map[string]interface{}); ok { 253 return m 254 } 255 return nil 256 default: 257 return nil 258 } 259 } 260 return dataMap 261 } 262 263 func getUsedMapOption(option ...MapOption) MapOption { 264 var usedOption MapOption 265 if len(option) > 0 { 266 usedOption = option[0] 267 } 268 return usedOption 269 } 270 271 type doMapConvertForMapOrStructValueInput struct { 272 IsRoot bool // It returns directly if it is not root and with no recursive converting. 273 Value interface{} // Current operation value. 274 RecursiveType recursiveType // The type from top function entry. 275 RecursiveOption bool // Whether convert recursively for `current` operation. 276 Option MapOption // Map converting option. 277 MustMapReturn bool // Must return map instead of Value when empty. 278 } 279 280 func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} { 281 if !in.IsRoot && !in.RecursiveOption { 282 return in.Value 283 } 284 285 var reflectValue reflect.Value 286 if v, ok := in.Value.(reflect.Value); ok { 287 reflectValue = v 288 in.Value = v.Interface() 289 } else { 290 reflectValue = reflect.ValueOf(in.Value) 291 } 292 reflectKind := reflectValue.Kind() 293 // If it is a pointer, we should find its real data type. 294 for reflectKind == reflect.Ptr { 295 reflectValue = reflectValue.Elem() 296 reflectKind = reflectValue.Kind() 297 } 298 switch reflectKind { 299 case reflect.Map: 300 var ( 301 mapKeys = reflectValue.MapKeys() 302 dataMap = make(map[string]interface{}) 303 ) 304 for _, k := range mapKeys { 305 var ( 306 mapKeyValue = reflectValue.MapIndex(k) 307 mapValue interface{} 308 ) 309 switch { 310 case mapKeyValue.IsZero(): 311 if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() { 312 // quick check for nil value. 313 mapValue = nil 314 } else { 315 // in case of: 316 // exception recovered: reflect: call of reflect.Value.Interface on zero Value 317 mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface() 318 } 319 default: 320 mapValue = mapKeyValue.Interface() 321 } 322 dataMap[String(k.Interface())] = doMapConvertForMapOrStructValue( 323 doMapConvertForMapOrStructValueInput{ 324 IsRoot: false, 325 Value: mapValue, 326 RecursiveType: in.RecursiveType, 327 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 328 Option: in.Option, 329 }, 330 ) 331 } 332 return dataMap 333 334 case reflect.Struct: 335 var dataMap = make(map[string]interface{}) 336 // Map converting interface check. 337 if v, ok := in.Value.(iMapStrAny); ok { 338 // Value copy, in case of concurrent safety. 339 for mapK, mapV := range v.MapStrAny() { 340 if in.RecursiveOption { 341 dataMap[mapK] = doMapConvertForMapOrStructValue( 342 doMapConvertForMapOrStructValueInput{ 343 IsRoot: false, 344 Value: mapV, 345 RecursiveType: in.RecursiveType, 346 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 347 Option: in.Option, 348 }, 349 ) 350 } else { 351 dataMap[mapK] = mapV 352 } 353 } 354 if len(dataMap) > 0 { 355 return dataMap 356 } 357 } 358 // Using reflect for converting. 359 var ( 360 rtField reflect.StructField 361 rvField reflect.Value 362 reflectType = reflectValue.Type() // attribute value type. 363 mapKey = "" // mapKey may be the tag name or the struct attribute name. 364 ) 365 for i := 0; i < reflectValue.NumField(); i++ { 366 rtField = reflectType.Field(i) 367 rvField = reflectValue.Field(i) 368 // Only convert the public attributes. 369 fieldName := rtField.Name 370 if !utils.IsLetterUpper(fieldName[0]) { 371 continue 372 } 373 mapKey = "" 374 fieldTag := rtField.Tag 375 for _, tag := range in.Option.Tags { 376 if mapKey = fieldTag.Get(tag); mapKey != "" { 377 break 378 } 379 } 380 if mapKey == "" { 381 mapKey = fieldName 382 } else { 383 // Support json tag feature: -, omitempty 384 mapKey = strings.TrimSpace(mapKey) 385 if mapKey == "-" { 386 continue 387 } 388 array := strings.Split(mapKey, ",") 389 if len(array) > 1 { 390 switch strings.TrimSpace(array[1]) { 391 case "omitempty": 392 if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) { 393 continue 394 } else { 395 mapKey = strings.TrimSpace(array[0]) 396 } 397 default: 398 mapKey = strings.TrimSpace(array[0]) 399 } 400 } 401 if mapKey == "" { 402 mapKey = fieldName 403 } 404 } 405 if in.RecursiveOption || rtField.Anonymous { 406 // Do map converting recursively. 407 var ( 408 rvAttrField = rvField 409 rvAttrKind = rvField.Kind() 410 ) 411 if rvAttrKind == reflect.Ptr { 412 rvAttrField = rvField.Elem() 413 rvAttrKind = rvAttrField.Kind() 414 } 415 switch rvAttrKind { 416 case reflect.Struct: 417 // Embedded struct and has no fields, just ignores it. 418 // Eg: gmeta.Meta 419 if rvAttrField.Type().NumField() == 0 { 420 continue 421 } 422 var ( 423 hasNoTag = mapKey == fieldName 424 // DO NOT use rvAttrField.Interface() here, 425 // as it might be changed from pointer to struct. 426 rvInterface = rvField.Interface() 427 ) 428 switch { 429 case hasNoTag && rtField.Anonymous: 430 // It means this attribute field has no tag. 431 // Overwrite the attribute with sub-struct attribute fields. 432 anonymousValue := doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{ 433 IsRoot: false, 434 Value: rvInterface, 435 RecursiveType: in.RecursiveType, 436 RecursiveOption: true, 437 Option: in.Option, 438 }) 439 if m, ok := anonymousValue.(map[string]interface{}); ok { 440 for k, v := range m { 441 dataMap[k] = v 442 } 443 } else { 444 dataMap[mapKey] = rvInterface 445 } 446 447 // It means this attribute field has desired tag. 448 case !hasNoTag && rtField.Anonymous: 449 dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{ 450 IsRoot: false, 451 Value: rvInterface, 452 RecursiveType: in.RecursiveType, 453 RecursiveOption: true, 454 Option: in.Option, 455 }) 456 457 default: 458 dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{ 459 IsRoot: false, 460 Value: rvInterface, 461 RecursiveType: in.RecursiveType, 462 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 463 Option: in.Option, 464 }) 465 } 466 467 // The struct attribute is type of slice. 468 case reflect.Array, reflect.Slice: 469 length := rvAttrField.Len() 470 if length == 0 { 471 dataMap[mapKey] = rvAttrField.Interface() 472 break 473 } 474 array := make([]interface{}, length) 475 for arrayIndex := 0; arrayIndex < length; arrayIndex++ { 476 array[arrayIndex] = doMapConvertForMapOrStructValue( 477 doMapConvertForMapOrStructValueInput{ 478 IsRoot: false, 479 Value: rvAttrField.Index(arrayIndex).Interface(), 480 RecursiveType: in.RecursiveType, 481 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 482 Option: in.Option, 483 }, 484 ) 485 } 486 dataMap[mapKey] = array 487 case reflect.Map: 488 var ( 489 mapKeys = rvAttrField.MapKeys() 490 nestedMap = make(map[string]interface{}) 491 ) 492 for _, k := range mapKeys { 493 nestedMap[String(k.Interface())] = doMapConvertForMapOrStructValue( 494 doMapConvertForMapOrStructValueInput{ 495 IsRoot: false, 496 Value: rvAttrField.MapIndex(k).Interface(), 497 RecursiveType: in.RecursiveType, 498 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 499 Option: in.Option, 500 }, 501 ) 502 } 503 dataMap[mapKey] = nestedMap 504 default: 505 if rvField.IsValid() { 506 dataMap[mapKey] = reflectValue.Field(i).Interface() 507 } else { 508 dataMap[mapKey] = nil 509 } 510 } 511 } else { 512 // No recursive map value converting 513 if rvField.IsValid() { 514 dataMap[mapKey] = reflectValue.Field(i).Interface() 515 } else { 516 dataMap[mapKey] = nil 517 } 518 } 519 } 520 if !in.MustMapReturn && len(dataMap) == 0 { 521 return in.Value 522 } 523 return dataMap 524 525 // The given value is type of slice. 526 case reflect.Array, reflect.Slice: 527 length := reflectValue.Len() 528 if length == 0 { 529 break 530 } 531 array := make([]interface{}, reflectValue.Len()) 532 for i := 0; i < length; i++ { 533 array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{ 534 IsRoot: false, 535 Value: reflectValue.Index(i).Interface(), 536 RecursiveType: in.RecursiveType, 537 RecursiveOption: in.RecursiveType == recursiveTypeTrue, 538 Option: in.Option, 539 }) 540 } 541 return array 542 } 543 return in.Value 544 } 545 546 // MapStrStr converts `value` to map[string]string. 547 // Note that there might be data copy for this map type converting. 548 func MapStrStr(value interface{}, option ...MapOption) map[string]string { 549 if r, ok := value.(map[string]string); ok { 550 return r 551 } 552 m := Map(value, option...) 553 if len(m) > 0 { 554 vMap := make(map[string]string, len(m)) 555 for k, v := range m { 556 vMap[k] = String(v) 557 } 558 return vMap 559 } 560 return nil 561 } 562 563 // MapStrStrDeep converts `value` to map[string]string recursively. 564 // Note that there might be data copy for this map type converting. 565 // Deprecated: used MapStrStr instead. 566 func MapStrStrDeep(value interface{}, tags ...string) map[string]string { 567 if r, ok := value.(map[string]string); ok { 568 return r 569 } 570 m := MapDeep(value, tags...) 571 if len(m) > 0 { 572 vMap := make(map[string]string, len(m)) 573 for k, v := range m { 574 vMap[k] = String(v) 575 } 576 return vMap 577 } 578 return nil 579 }