• 一、前言
  • 二、结构体转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. 活动识别API服务开发
  2. 2019.03.01 bzoj2555: SubString(sam+lct)
  3. 可构造样式表 - 通过javascript来生成css的新方式
  4. [力扣] 二叉树的层序遍历
  5. linux文件系统简介
  6. 【C】——C项目中的菜单功能(源码)
  7. java guava 使用_使用Guava操作基本类型
  8. 单元测试Fluent验证
  9. leetcode刷题:循环队列
  10. vue学习笔记-7-分支结构
  11. 10个不错的免费在线翻译网站
  12. 企业微信API全局错误码 enum枚举类
  13. 17 CoCos Creator-Node Tree 层级管理器
  14. Java基础题练习(switch多选择结构的使用、white循环中++的使用、white循环中输入分数游戏)
  15. php做网络心理测试,php心理测试程序源代码版,求高手帮忙写一个c语言的心理测试程序...
  16. 微信小程序-加载图片
  17. java 断言 assert 初步使用:断言开启、断言使用
  18. addrow是什么意思java_如何在JTable中添加行?
  19. The non-abstract class ‘FwfhTextStyle‘ is missing implementations for these
  20. 批量更改文件夹/文件的名字

热门文章

  1. 最大连续区间和算法详解+代码
  2. oracle 输出入参怎么抛异常,oracle 存储过程入参 pids , varchar2 类型,where in 条件 会抛异常...
  3. cpu win10 安装yolo_Win10 超详细 0基础 搭建YOLOV5教程【环境搭建篇】
  4. oracle sql 取最大分组,oracle sql 按某个字段分组然后从每组取出最大的一条纪录...
  5. mysql load data infile 上传数据 不显示_第22问:我有带外键的表,你有数据么?
  6. yii2 获取模块名,控制名,动作方法名
  7. 如何配置filezilla服务端和客户端
  8. 使用 Java8的 stream对list数据去重,使用filter()过滤列表,list转map
  9. ExecutorService中submit和execute的区别
  10. 软件工程网络15个人作业3--案例分析