// 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 }