Go中GoFrameMap转换详解

 更新时间:2026年05月18日 08:41:26   作者:念何架构之路  
本文详细解析了Go语言中gconv.Map()方法的实现原理,该方法通过递归处理将各种数据类型转换为map[string]any格式,感兴趣的可以了解一下

示例:

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()方法:

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

因为调用没有传入这个值.然后通过断点验证可以知道option为nil.

getUsedMapOption()方法:

func getUsedMapOption(option ...MapOption) MapOption {
    var usedOption = MapOption{
       ContinueOnError: true,
    }
    if len(option) > 0 {
       usedOption = option[0]
    }
    return usedOption
}

defaultConverter.Map()方法:

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

RecursiveTypeAuto的值提前定义好的.

c.doMapConvert()方法:

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:

var StructTagPriority = []string{
    GConv, Param, GConvShort, ParamShort, Json,
}

MapOption结构体:

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()方法:

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
}

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

到此这篇关于Go中GoFrameMap转换详解的文章就介绍到这了,更多相关GoFrameMap转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang实现四层负载均衡的示例代码

    Golang实现四层负载均衡的示例代码

    做开发的同学应该经常听到过负载均衡的概念,今天我们就来实现一个乞丐版的四层负载均衡,并用它对mysql进行负载均衡测试,感兴趣的可以了解一下
    2023-07-07
  • Golang结合ip2region实现ip归属地查询

    Golang结合ip2region实现ip归属地查询

    ip2region - 是一个离线IP地址定位库和IP定位数据管理框架,提供了众多主流编程语言的 xdb 数据生成和查询客户端实现,下面我们就来看看Golang如何结合ip2region实现ip归属地查询吧
    2024-03-03
  • Golang搭建HTTP服务器

    Golang搭建HTTP服务器

    Golang是一种非常流行的编程语言,它的开发速度快,代码运行效率高等特点非常适合做Web应用的开发。本文将介绍如何使用Golang搭建HTTP服务器,需要的朋友可以参考阅读
    2023-04-04
  • Go调度器学习之协作与抢占详解

    Go调度器学习之协作与抢占详解

    如果某个G执行时间过长,其他的G如何才能被正常调度,这就引出了接下来的话题:协作与抢占。本文将通过一些示例为大家详细讲讲调度器中协作与抢占的相关知识,需要的可以参考一下
    2023-04-04
  • Go 中闭包的底层原理

    Go 中闭包的底层原理

    这篇文章主要介绍了Go 中闭包的底层原理,闭包的基本原理是一种现象,一个函数内引用了外部的局部变量的现象,带着些许的了解和小编一起进入文章正题学习
    2021-10-10
  • GoLand IDE 无法识别 Go工作区中的引用(解决方案)

    GoLand IDE 无法识别 Go工作区中的引用(解决方案)

    GoLand因未识别go.work工作区导致未解析引用错误,需通过正确配置工作区目录或使用replace语句解决,最佳实践是启用GoModulesIntegration,确保IDE与命令行工具一致,本文给大家介绍GoLand IDE无法识别Go工作区中的引用问题,感兴趣的朋友一起看看吧
    2025-09-09
  • Go语言之Goroutine与信道异常处理

    Go语言之Goroutine与信道异常处理

    这篇文章主要给大家介绍得是Go语言Goroutine与信道异常处理,Goroutine 之间通信是通过 channel 通信的,想详细了解得小伙伴跟小编一起来学习下面文章内容吧
    2021-10-10
  • Go1.18都出泛型了速来围观

    Go1.18都出泛型了速来围观

    泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型,本文通过例子给大家介绍下如何使用泛型,对Go1.18泛型相关知识感兴趣的朋友一起看看吧
    2022-03-03
  • go内存缓存BigCache封装Entry源码解读

    go内存缓存BigCache封装Entry源码解读

    这篇文章主要为大家介绍了go内存缓存BigCache封装Entry源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • golang实现简单的udp协议服务端与客户端示例

    golang实现简单的udp协议服务端与客户端示例

    这篇文章主要介绍了golang实现简单的udp协议服务端与客户端,结合实例形式分析了基于UDP协议的数据传输相关实现技巧,需要的朋友可以参考下
    2016-07-07

最新评论