示例:
go
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
func main() {
type User struct {
Uid string `c:"uid"`
Name string `c:"name"`
}
//对象.
g.Dump(gconv.Map(User{Uid: "1", Name: "john"}))
}
输出结果:

gconv.Map()方法:
go
func Map(value any, option ...MapOption) map[string]any {
result, _ := defaultConverter.Map(value, getUsedMapOption(option...))
return result
}

因为调用没有传入这个值.然后通过断点验证可以知道option为nil.
getUsedMapOption()方法:
go
func getUsedMapOption(option ...MapOption) MapOption {
var usedOption = MapOption{
ContinueOnError: true,
}
if len(option) > 0 {
usedOption = option[0]
}
return usedOption
}

defaultConverter.Map()方法:
go
func (c *Converter) Map(value any, option ...MapOption) (map[string]any, error) {
return c.doMapConvert(value, RecursiveTypeAuto, false, c.getMapOption(option...))
}

RecursiveTypeAuto的值提前定义好的.
c.doMapConvert()方法:
go
func (c *Converter) doMapConvert(
value any, recursive RecursiveType, mustMapReturn bool, option MapOption,
) (map[string]any, error) {
if value == nil {
return nil, nil
}
// It redirects to its underlying value if it has implemented interface iVal.
if v, ok := value.(localinterface.IVal); ok {
value = v.Val()
}
var (
err error
newTags = gtag.StructTagPriority
)
if option.Deep {
recursive = RecursiveTypeTrue
}
switch len(option.Tags) {
case 0:
// No need handling.
case 1:
newTags = append(strings.Split(option.Tags[0], ","), gtag.StructTagPriority...)
default:
newTags = append(option.Tags, gtag.StructTagPriority...)
}
// Assert the common combination of types, and finally it uses reflection.
dataMap := make(map[string]any)
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, err
}
} else {
return nil, 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, err
}
} else {
return nil, nil
}
case map[any]any:
recursiveOption := option
recursiveOption.Tags = newTags
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == RecursiveTypeTrue,
Option: recursiveOption,
},
)
if err != nil && !option.ContinueOnError {
return nil, err
}
}
case map[any]string:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = v
}
case map[any]int:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = v
}
case map[any]uint:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = v
}
case map[any]float32:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = v
}
case map[any]float64:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = 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]string:
for k, v := range r {
dataMap[k] = v
}
case map[string]any:
if recursive == RecursiveTypeTrue {
recursiveOption := option
recursiveOption.Tags = newTags
// A copy of current map.
for k, v := range r {
dataMap[k], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == RecursiveTypeTrue,
Option: recursiveOption,
},
)
if err != nil && !option.ContinueOnError {
return nil, err
}
}
} else {
// It returns the map directly without any changing.
return r, nil
}
case map[int]any:
recursiveOption := option
recursiveOption.Tags = newTags
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == RecursiveTypeTrue,
Option: recursiveOption,
},
)
if err != nil && !option.ContinueOnError {
return nil, err
}
}
case map[int]string:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = v
}
case map[uint]string:
for k, v := range r {
s, err := c.String(k)
if err != nil && !option.ContinueOnError {
return nil, err
}
dataMap[s] = 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.Pointer {
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]any{"k1":"v1", "k2":"v2"}
// []string{"k1","v1","k2"} => map[string]any{"k1":"v1", "k2":nil}
case reflect.Slice, reflect.Array:
length := reflectValue.Len()
for i := 0; i < length; i += 2 {
s, err := c.String(reflectValue.Index(i).Interface())
if err != nil && !option.ContinueOnError {
return nil, err
}
if i+1 < length {
dataMap[s] = reflectValue.Index(i + 1).Interface()
} else {
dataMap[s] = nil
}
}
case reflect.Map, reflect.Struct, reflect.Interface:
recursiveOption := option
recursiveOption.Tags = newTags
convertedValue, err := c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: true,
Value: value,
RecursiveType: recursive,
RecursiveOption: recursive == RecursiveTypeTrue,
Option: recursiveOption,
MustMapReturn: mustMapReturn,
},
)
if err != nil && !option.ContinueOnError {
return nil, err
}
if m, ok := convertedValue.(map[string]any); ok {
return m, nil
}
return nil, nil
default:
return nil, nil
}
}
return dataMap, nil
}

gtag.StructTagPriority:
csharp
var StructTagPriority = []string{
GConv, Param, GConvShort, ParamShort, Json,
}
MapOption结构体:
go
type MapOption struct {
// Deep marks doing Map function recursively, which means if the attribute of given converting value
// is also a struct/*struct, it automatically calls Map function on this attribute converting it to
// a map[string]any type variable.
Deep bool
// OmitEmpty ignores the attributes that has json `omitempty` tag.
OmitEmpty bool
// Tags specifies the converted map key name by struct tag name.
Tags []string
// ContinueOnError specifies whether to continue converting the next element
// if one element converting fails.
ContinueOnError bool
}
上面获取这个结构体的时候.因为没有给Deep赋值.所以这里都默认为false.所以recursive还是auto.







反射处理默认分支.
首先将 value 转为 reflect.Value(若已是 reflect.Value 则直接使用)。
引用指针,直到得到非指针的 reflectValue 和 reflectKind。
根据 reflectKind 分情况:reflect.Slice 或 reflect.Array
将数组/切片视为键值交替的序列:
偶数索引为键,奇数索引为值(例如 ["k1","v1","k2","v2"] → {"k1":"v1","k2":"v2"})。
若长度为奇数,最后一个键对应的值为 nil。
每个键通过 c.String() 转换为字符串,若出错且不忽略错误则返回。
reflect.Map、reflect.Struct、reflect.Interface
调用 c.doMapConvertForMapOrStructValue 进行转换,该函数会递归处理 map/struct,并根据 recursive 和 MustMapReturn 决定最终输出。
若返回的值确实是 map[string]any,则返回它;否则返回 nil, nil。
其他类型
直接返回 nil, nil,不进行转换。
doMapConvertForMapOrStructValue()方法:
php
func (c *Converter) doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) (any, error) {
if !in.IsRoot && !in.RecursiveOption {
return in.Value, nil
}
var (
err error
reflectValue reflect.Value
)
if v, ok := in.Value.(reflect.Value); ok {
reflectValue = v
in.Value = v.Interface()
} else {
reflectValue = reflect.ValueOf(in.Value)
}
reflectKind := reflectValue.Kind()
// If it is a pointer, we should find its real data type.
for reflectKind == reflect.Pointer {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Map:
var (
mapIter = reflectValue.MapRange()
dataMap = make(map[string]any)
)
for mapIter.Next() {
var (
mapKeyValue = mapIter.Value()
mapValue any
)
switch {
case mapKeyValue.IsZero():
if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
// quick check for nil value.
mapValue = nil
} else {
// in case of:
// exception recovered: reflect: call of reflect.Value.Interface on zero Value
mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
}
default:
mapValue = mapKeyValue.Interface()
}
s, err := c.String(mapIter.Key().Interface())
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
dataMap[s], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapValue,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
}
return dataMap, nil
case reflect.Struct:
var dataMap = make(map[string]any)
// Map converting interface check.
if v, ok := in.Value.(localinterface.IMapStrAny); ok {
// Value copy, in case of concurrent safety.
for mapK, mapV := range v.MapStrAny() {
if in.RecursiveOption {
dataMap[mapK], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapV,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
} else {
dataMap[mapK] = mapV
}
}
if len(dataMap) > 0 {
return dataMap, nil
}
}
// Using reflect for converting.
var (
rtField reflect.StructField
rvField reflect.Value
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 in.Option.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 in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
continue
} else {
mapKey = strings.TrimSpace(array[0])
}
default:
mapKey = strings.TrimSpace(array[0])
}
}
if mapKey == "" {
mapKey = fieldName
}
}
if in.RecursiveOption || rtField.Anonymous {
// Do map converting recursively.
var (
rvAttrField = rvField
rvAttrKind = rvField.Kind()
)
if rvAttrKind == reflect.Pointer {
rvAttrField = rvField.Elem()
rvAttrKind = rvAttrField.Kind()
}
switch rvAttrKind {
case reflect.Struct:
// Embedded struct and has no fields, just ignores it.
// Eg: gmeta.Meta
if rvAttrField.Type().NumField() == 0 {
continue
}
var (
hasNoTag = mapKey == fieldName
// DO NOT use rvAttrField.Interface() here,
// as it might be changed from pointer to struct.
rvInterface = rvField.Interface()
)
switch {
case hasNoTag && rtField.Anonymous:
// It means this attribute field has no tag.
// Overwrite the attribute with sub-struct attribute fields.
anonymousValue, err := c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
if m, ok := anonymousValue.(map[string]any); ok {
for k, v := range m {
dataMap[k] = v
}
} else {
dataMap[mapKey] = rvInterface
}
// It means this attribute field has desired tag.
case !hasNoTag && rtField.Anonymous:
dataMap[mapKey], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
default:
dataMap[mapKey], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
}
// The struct attribute is type of slice.
case reflect.Array, reflect.Slice:
length := rvAttrField.Len()
if length == 0 {
dataMap[mapKey] = rvAttrField.Interface()
break
}
array := make([]any, length)
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
array[arrayIndex], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvAttrField.Index(arrayIndex).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
}
dataMap[mapKey] = array
case reflect.Map:
var (
mapIter = rvAttrField.MapRange()
nestedMap = make(map[string]any)
)
for mapIter.Next() {
s, err := c.String(mapIter.Key().Interface())
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
nestedMap[s], err = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapIter.Value().Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
},
)
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
}
dataMap[mapKey] = nestedMap
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 !in.MustMapReturn && len(dataMap) == 0 {
return in.Value, nil
}
return dataMap, nil
// The given value is type of slice.
case reflect.Array, reflect.Slice:
length := reflectValue.Len()
if length == 0 {
break
}
array := make([]any, reflectValue.Len())
for i := 0; i < length; i++ {
array[i], err = c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: reflectValue.Index(i).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
Option: in.Option,
})
if err != nil && !in.Option.ContinueOnError {
return nil, err
}
}
return array, nil
default:
}
return in.Value, nil
}





下面又会对值进行各种类型的判断转换.可以参考上面的.

语雀地址www.yuque.com/itbosunmian...?
《Go.》 密码:xbkk 欢迎大家访问.提意见.