• 一、前言
  • 二、结构体转map[string]interface{}
    • 1、JSON序列化方式
    • 2、反射
    • 3、第三方库structs
  • 三、嵌套结构体转map[string]interface{}
    • 1、前言
    • 2、第三方库structs
    • 3、使用反射转成单层map

一、前言

原文地址:结构体转map[string]interface{}的若干方法

本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的“坑”,也有你需要知道的若干方法。

我们在Go语言中通常使用结构体来保存我们的数据,例如要存储用户信息,我们可能会定义如下结构体:

// UserInfo 用户信息
type UserInfo struct {Name string `json:"name"`Age  int    `json:"age"`
}u1 := UserInfo{Name: "q1mi", Age: 18}

假设现在要将上面的u1转换成map[string]interface{},该如何操作呢?

二、结构体转map[string]interface{}

1、JSON序列化方式

这不是很简单吗?我用JSON序列化一下u1,再反序列化成map不就可以了么。说干就干,代码如下:

func main() {u1 := UserInfo{Name: "q1mi", Age: 18}b, _ := json.Marshal(&u1)var m map[string]interface{}_ = json.Unmarshal(b, &m)for k, v := range m{fmt.Printf("key:%v value:%v\n", k, v)}
}

输出:

key:name value:q1mi
key:age value:18

看起来没什么问题,但其实这里是有一个“坑”的。那就是Go语言中的json包在序列化空接口存放的数字类型(整型、浮点型等)都会序列化成float64类型。

也就是上面例子中m["age"]现在底层是一个float64了,不是个int了。我们来验证下:

func main() {u1 := UserInfo{Name: "q1mi", Age: 18}b, _ := json.Marshal(&u1)var m map[string]interface{}_ = json.Unmarshal(b, &m)for k, v := range m{fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)}
}

输出:

key:name value:q1mi value type:string
key:age value:18 value type:float64

很显然,出现了一个意料之外的行为,我们需要想办法规避掉。

2、反射

没办法就需要自己动手去实现了。这里使用反射遍历结构体字段的方式生成map,具体代码如下:

// ToMap 结构体转为Map[string]interface{}
func ToMap(in interface{}, tagName string) (map[string]interface{}, error){out := make(map[string]interface{})v := reflect.ValueOf(in)if v.Kind() == reflect.Ptr {v = v.Elem()}if v.Kind() != reflect.Struct {  // 非结构体返回错误提示return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)}t := v.Type()// 遍历结构体字段// 指定tagName值为map中key;字段值为map中valuefor i := 0; i < v.NumField(); i++ {fi := t.Field(i)if tagValue := fi.Tag.Get(tagName); tagValue != "" {out[tagValue] = v.Field(i).Interface()}}return out, nil
}

验证一下:

m2, _ := ToMap(&u1, "json")
for k, v := range m2{fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}

输出:

key:name value:q1mi value type:string
key:age value:18 value type:int

这一次map["age"]的类型就对了的。

3、第三方库structs

除了自己实现,Github上也有现成的轮子,例如第三方库:https://github.com/fatih/structs。

它使用的自定义结构体tagstructs:

// UserInfo 用户信息
type UserInfo struct {Name string `json:"name" structs:"name"`Age  int    `json:"age" structs:"age"`
}

用法很简单:

m3 := structs.Map(&u1)
for k, v := range m3 {fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}

structs这个包也有很多其他的使用示例,大家可以去查看文档。但是需要注意的是目前这个库已经被作者设置为只读了。

三、嵌套结构体转map[string]interface{}

1、前言

structs本身是支持嵌套结构体转map[string]interface{}的,遇到结构体嵌套它会转换为map[string]interface{}嵌套map[string]interface{}的模式。

我们定义一组嵌套的结构体如下:

// UserInfo 用户信息
type UserInfo struct {Name string `json:"name" structs:"name"`Age  int    `json:"age" structs:"age"`Profile `json:"profile" structs:"profile"`
}// Profile 配置信息
type Profile struct {Hobby string `json:"hobby" structs:"hobby"`
}

声明结构体变量u1:

u1 := UserInfo{Name: "q1mi", Age: 18, Profile: Profile{"双色球"}}

2、第三方库structs

代码和上面的其实是一样的:

m3 := structs.Map(&u1)
for k, v := range m3 {fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}

输出结果:

key:name value:q1mi value type:string
key:age value:18 value type:int
key:profile value:map[hobby:双色球] value type:map[string]interface {}

从结果来看最后嵌套字段profilemap[string]interface {},属于map嵌套map。

3、使用反射转成单层map

如果我们想把嵌套的结构体转换成一个单层map该怎么做呢?

我们把上面反射的代码稍微修改一下就可以了:

// ToMap2 将结构体转为单层map
func ToMap2(in interface{}, tag string) (map[string]interface{}, error) {// 当前函数只接收struct类型v := reflect.ValueOf(in)if v.Kind() == reflect.Ptr { // 结构体指针v = v.Elem()}if v.Kind() != reflect.Struct {return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)}out := make(map[string]interface{})queue := make([]interface{}, 0, 1)queue = append(queue, in)for len(queue) > 0 {v := reflect.ValueOf(queue[0])if v.Kind() == reflect.Ptr { // 结构体指针v = v.Elem()}queue = queue[1:]t := v.Type()for i := 0; i < v.NumField(); i++ {vi := v.Field(i)if vi.Kind() == reflect.Ptr { // 内嵌指针vi = vi.Elem()if vi.Kind() == reflect.Struct { // 结构体queue = append(queue, vi.Interface())} else {ti := t.Field(i)if tagValue := ti.Tag.Get(tag); tagValue != "" {// 存入mapout[tagValue] = vi.Interface()}}break}if vi.Kind() == reflect.Struct { // 内嵌结构体queue = append(queue, vi.Interface())break}// 一般字段ti := t.Field(i)if tagValue := ti.Tag.Get(tag); tagValue != "" {// 存入mapout[tagValue] = vi.Interface()}}}return out, nil
}

测试一下:

m4, _ := ToMap2(&u1, "json")
for k, v := range m4 {fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}

输出:

key:name value:q1mi value type:string
key:age value:18 value type:int
key:hobby value:双色球 value type:string

这下我们就把嵌套的结构体转为单层的map了,但是要注意这种场景下结构体和嵌套结构体的字段就需要避免重复

结构体转map[string]interface{}的若干方法相关推荐

  1. 技巧:Go 结构体如何转换成 map[string]interface{}

    本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的"坑",也有你需要知道的若干方法. 我们在Go语言中通常使用结构体来保存我们的数据,例如要 ...

  2. Golang——结构体创建与初始化、结构体与数组、结构体与切片、结构体与map、结构体作为函数参数、结构体方法、结构体方法继承

    结构体: 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合 结构体可以很好的管理一批有联系的数据,使用结构体可以提高程序的易读性,类似于Java的类一样 不能在结构体直接赋值 字段名必须唯一 ...

  3. go语言解析 map[string]interface{} 数据格式

    注意事项 map记得分配内存 解析出来的int类型会变成float64类型 注意判断不为nil后再转换类型 package mainimport ("fmt""encod ...

  4. 2022-11-14:rust语言,请使用过程宏给结构体AAA生成结构体AAABuilder和创建AAABuilder实例的方法。 宏使用如下: #[derive(Builder)] pub stru

    2022-11-14:rust语言,请使用过程宏给结构体AAA生成结构体AAABuilder和创建AAABuilder实例的方法. 宏使用如下: #[derive(Builder)] pub stru ...

  5. 专科 java转go 翱翔之路(一)基础语法:变量声明,匿名函数,结构体,函数,map

    本人专科!在太原干了一年了,想从java转go,上班中自学go,明年找go语言相关的工作 立帖为证! 待羽翼丰满,大风到来,便是我翱翔之时!!! 1.Go语言 1.命令 1.1 查看版本号 go ve ...

  6. Golang结构体和map

    Golang 文章目录 Golang 1 struct 2 map 3 struct补充 1 struct 在Golang中没有对象,但是有面向对象的思想,有继承,多态,封装的思想. 但是缺少了cla ...

  7. c语言结构体作为形参是否加struct_(struct)结构体变量作为函数参数调用的方法小结...

    结构体变量作为函数参数传递的3种方法将一个结构体变量中的数据传递给另一个函数,有下列3种方法:用结构体变量名作参数.一般较少用这种方法.用指向结构体变量 结构体变量.结构指针变量.结构数组作为函数的参 ...

  8. C语言结构体的作用、定义、使用方法以及实例

    目前已学的数据类型:数组(1.所有数据的类型必须一致   2.访问数组的成员必须通过下标) 学籍管理系统:学生(姓名.学号.性别.年龄.成绩...) 飞机票订票系统:机票(订票时间.班号.起点.终点. ...

  9. k8s之CRD定义map[string]interface{}类型

    CRD中定义 compStatus: type: object x-kubernetes-preserve-unknown-fields: true Controller type的定义 // +ku ...

  10. golang 结构体 map 转化为 json

    目录 结构体生成json json转成结构体 map生成json json转化为map 结构体生成json package mainimport ("encoding/json"& ...

最新文章

  1. 土木工程计算机仿真学科未来前景,土木工程的发展现状与未来发展趋势
  2. 云端卫士DDoS防护解决方案助力互联网金融安全
  3. 8 月--菜鸟吐槽日志
  4. 【Android 逆向】ART 脱壳 ( dex2oat 脱壳 | aosp 中搜索 dex2oat 源码 | dex2oat.cc#main 主函数源码 )
  5. SAP ABAP SQL查询分析器
  6. 使用CGContext画线操作小记
  7. 那些年收藏的技术文章(一) CSDN篇
  8. 从服务器恢复系统,服务器恢复系统
  9. 【Elasticsearch】父子聚合 can‘t specify parent if no parent field has been configured
  10. 学javascript看什么书?
  11. Apache ab 使用说明
  12. 物联网老年人健康管理系统源码
  13. 魅族mx4pro刷linux,老树开新花 魅族MX4 Pro刷Flyme 5体验
  14. 计算机中利用的物理原理,现代电脑技术中物理原理.doc
  15. Ubuntu安装LXDE桌面环境
  16. SEO人员快速提升关键词优化排名的方法
  17. SQL server课程设计-景点门票销售管理系统(基于Javagui制作)
  18. pythongbk编码怎么解决_如何解决 Python print 输出文本显示 gbk 编码错误
  19. Python类和对象使用
  20. cassandra安装及配置

热门文章

  1. ZOJ4037 Peer Review
  2. java配环境变量_用于 Java 开发的配置工具 Simple Configuration Facade
  3. spark sql 给dataframe列重命名的三种方式
  4. java,python,scala发送http请求
  5. 多线程 空值线程数_跳槽涨薪季面试题之多线程(三)
  6. oracle按特定字符截取字符串_LeetCode基础算法题第159篇:找出由特定字符数组组成的字符串...
  7. 佐客牛排机器人餐厅_3分钟出餐!全球首家机器人餐厅开业
  8. 自适应图片九宫格 css,高度自适应的九宫格效果
  9. 顺序队列为空的条件_C语言实现顺序队列
  10. 模拟退火算法_Simulated Annealing 模拟退火算法