123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- package gconv
- import (
- "github.com/gogf/gf/internal/json"
- "reflect"
- "strings"
- "github.com/gogf/gf/internal/empty"
- "github.com/gogf/gf/internal/utils"
- )
- // Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
- // map/struct/*struct type, then the conversion will fail and returns nil.
- //
- // If `value` is a struct/*struct object, the second parameter `tags` specifies the most priority
- // tags that will be detected, otherwise it detects the tags in order of:
- // gconv, json, field name.
- func Map(value interface{}, tags ...string) map[string]interface{} {
- return doMapConvert(value, false, tags...)
- }
- // MapDeep does Map function recursively, which means if the attribute of `value`
- // is also a struct/*struct, calls Map function on this attribute converting it to
- // a map[string]interface{} type variable.
- // Also see Map.
- func MapDeep(value interface{}, tags ...string) map[string]interface{} {
- return doMapConvert(value, true, tags...)
- }
- // doMapConvert implements the map converting.
- // It automatically checks and converts json string to map if `value` is string/[]byte.
- //
- // TODO completely implement the recursive converting for all types, especially the map.
- func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} {
- if value == nil {
- return nil
- }
- newTags := StructTagPriority
- switch len(tags) {
- case 0:
- // No need handling.
- case 1:
- newTags = append(strings.Split(tags[0], ","), StructTagPriority...)
- default:
- newTags = append(tags, StructTagPriority...)
- }
- // Assert the common combination of types, and finally it uses reflection.
- dataMap := make(map[string]interface{})
- switch r := value.(type) {
- case string:
- // If it is a JSON string, automatically unmarshal it!
- if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
- if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
- return nil
- }
- } else {
- return nil
- }
- case []byte:
- // If it is a JSON string, automatically unmarshal it!
- if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
- if err := json.UnmarshalUseNumber(r, &dataMap); err != nil {
- return nil
- }
- } else {
- return nil
- }
- case map[interface{}]interface{}:
- for k, v := range r {
- dataMap[String(k)] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
- }
- case map[interface{}]string:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[interface{}]int:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[interface{}]uint:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[interface{}]float32:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[interface{}]float64:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[string]bool:
- for k, v := range r {
- dataMap[k] = v
- }
- case map[string]int:
- for k, v := range r {
- dataMap[k] = v
- }
- case map[string]uint:
- for k, v := range r {
- dataMap[k] = v
- }
- case map[string]float32:
- for k, v := range r {
- dataMap[k] = v
- }
- case map[string]float64:
- for k, v := range r {
- dataMap[k] = v
- }
- case map[string]interface{}:
- if recursive {
- // A copy of current map.
- for k, v := range r {
- dataMap[k] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
- }
- } else {
- // It returns the map directly without any changing.
- return r
- }
- case map[int]interface{}:
- for k, v := range r {
- dataMap[String(k)] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
- }
- case map[int]string:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- case map[uint]string:
- for k, v := range r {
- dataMap[String(k)] = v
- }
- default:
- // Not a common type, it then uses reflection for conversion.
- var reflectValue reflect.Value
- if v, ok := value.(reflect.Value); ok {
- reflectValue = v
- } else {
- reflectValue = reflect.ValueOf(value)
- }
- reflectKind := reflectValue.Kind()
- // If it is a pointer, we should find its real data type.
- for reflectKind == reflect.Ptr {
- reflectValue = reflectValue.Elem()
- reflectKind = reflectValue.Kind()
- }
- switch reflectKind {
- // If `value` is type of array, it converts the value of even number index as its key and
- // the value of odd number index as its corresponding value, for example:
- // []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
- // []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
- case reflect.Slice, reflect.Array:
- length := reflectValue.Len()
- for i := 0; i < length; i += 2 {
- if i+1 < length {
- dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface()
- } else {
- dataMap[String(reflectValue.Index(i).Interface())] = nil
- }
- }
- case reflect.Map, reflect.Struct, reflect.Interface:
- convertedValue := doMapConvertForMapOrStructValue(true, value, recursive, newTags...)
- if m, ok := convertedValue.(map[string]interface{}); ok {
- return m
- }
- return nil
- default:
- return nil
- }
- }
- return dataMap
- }
- func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive bool, tags ...string) interface{} {
- if isRoot == false && recursive == false {
- return value
- }
- var reflectValue reflect.Value
- if v, ok := value.(reflect.Value); ok {
- reflectValue = v
- value = v.Interface()
- } else {
- reflectValue = reflect.ValueOf(value)
- }
- reflectKind := reflectValue.Kind()
- // If it is a pointer, we should find its real data type.
- for reflectKind == reflect.Ptr {
- reflectValue = reflectValue.Elem()
- reflectKind = reflectValue.Kind()
- }
- switch reflectKind {
- case reflect.Map:
- var (
- mapKeys = reflectValue.MapKeys()
- dataMap = make(map[string]interface{})
- )
- for _, k := range mapKeys {
- dataMap[String(k.Interface())] = doMapConvertForMapOrStructValue(
- false,
- reflectValue.MapIndex(k).Interface(),
- recursive,
- tags...,
- )
- }
- if len(dataMap) == 0 {
- return value
- }
- return dataMap
- case reflect.Struct:
- // Map converting interface check.
- if v, ok := value.(apiMapStrAny); ok {
- m := v.MapStrAny()
- if recursive {
- for k, v := range m {
- m[k] = doMapConvertForMapOrStructValue(false, v, recursive, tags...)
- }
- }
- return m
- }
- // Using reflect for converting.
- var (
- rtField reflect.StructField
- rvField reflect.Value
- dataMap = make(map[string]interface{}) // result map.
- reflectType = reflectValue.Type() // attribute value type.
- mapKey = "" // mapKey may be the tag name or the struct attribute name.
- )
- for i := 0; i < reflectValue.NumField(); i++ {
- rtField = reflectType.Field(i)
- rvField = reflectValue.Field(i)
- // Only convert the public attributes.
- fieldName := rtField.Name
- if !utils.IsLetterUpper(fieldName[0]) {
- continue
- }
- mapKey = ""
- fieldTag := rtField.Tag
- for _, tag := range tags {
- if mapKey = fieldTag.Get(tag); mapKey != "" {
- break
- }
- }
- if mapKey == "" {
- mapKey = fieldName
- } else {
- // Support json tag feature: -, omitempty
- mapKey = strings.TrimSpace(mapKey)
- if mapKey == "-" {
- continue
- }
- array := strings.Split(mapKey, ",")
- if len(array) > 1 {
- switch strings.TrimSpace(array[1]) {
- case "omitempty":
- if empty.IsEmpty(rvField.Interface()) {
- continue
- } else {
- mapKey = strings.TrimSpace(array[0])
- }
- default:
- mapKey = strings.TrimSpace(array[0])
- }
- }
- }
- if recursive || rtField.Anonymous {
- // Do map converting recursively.
- var (
- rvAttrField = rvField
- rvAttrKind = rvField.Kind()
- )
- if rvAttrKind == reflect.Ptr {
- rvAttrField = rvField.Elem()
- rvAttrKind = rvAttrField.Kind()
- }
- switch rvAttrKind {
- case reflect.Struct:
- var (
- hasNoTag = mapKey == fieldName
- rvAttrInterface = rvAttrField.Interface()
- )
- if hasNoTag && rtField.Anonymous {
- // It means this attribute field has no tag.
- // Overwrite the attribute with sub-struct attribute fields.
- anonymousValue := doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
- if m, ok := anonymousValue.(map[string]interface{}); ok {
- for k, v := range m {
- dataMap[k] = v
- }
- } else {
- dataMap[mapKey] = rvAttrInterface
- }
- } else if !hasNoTag && rtField.Anonymous {
- // It means this attribute field has desired tag.
- dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
- } else {
- dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, recursive, tags...)
- }
- // The struct attribute is type of slice.
- case reflect.Array, reflect.Slice:
- length := rvField.Len()
- if length == 0 {
- dataMap[mapKey] = rvField.Interface()
- break
- }
- array := make([]interface{}, length)
- for i := 0; i < length; i++ {
- array[i] = doMapConvertForMapOrStructValue(false, rvField.Index(i), recursive, tags...)
- }
- dataMap[mapKey] = array
- default:
- if rvField.IsValid() {
- dataMap[mapKey] = reflectValue.Field(i).Interface()
- } else {
- dataMap[mapKey] = nil
- }
- }
- } else {
- // No recursive map value converting
- if rvField.IsValid() {
- dataMap[mapKey] = reflectValue.Field(i).Interface()
- } else {
- dataMap[mapKey] = nil
- }
- }
- }
- if len(dataMap) == 0 {
- return value
- }
- return dataMap
- // The given value is type of slice.
- case reflect.Array, reflect.Slice:
- length := reflectValue.Len()
- if length == 0 {
- break
- }
- array := make([]interface{}, reflectValue.Len())
- for i := 0; i < length; i++ {
- array[i] = doMapConvertForMapOrStructValue(false, reflectValue.Index(i), recursive, tags...)
- }
- return array
- }
- return value
- }
- // MapStrStr converts `value` to map[string]string.
- // Note that there might be data copy for this map type converting.
- func MapStrStr(value interface{}, tags ...string) map[string]string {
- if r, ok := value.(map[string]string); ok {
- return r
- }
- m := Map(value, tags...)
- if len(m) > 0 {
- vMap := make(map[string]string, len(m))
- for k, v := range m {
- vMap[k] = String(v)
- }
- return vMap
- }
- return nil
- }
- // MapStrStrDeep converts `value` to map[string]string recursively.
- // Note that there might be data copy for this map type converting.
- func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
- if r, ok := value.(map[string]string); ok {
- return r
- }
- m := MapDeep(value, tags...)
- if len(m) > 0 {
- vMap := make(map[string]string, len(m))
- for k, v := range m {
- vMap[k] = String(v)
- }
- return vMap
- }
- return nil
- }
|