Go结构体如何优雅的转map[string]interface{}

3,054 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情

前言

今天在撸代码的时候遇到一个问题,在使用json将结构体转为map的时候发现前后类型发生了变化,这是怎么回事?今天就来看一下在go中将结构体转为map的几种方式,在开发中还是常用的,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

结构体转map[string]interface{}

为了方便下面测试,我们先定义一个结构体如下:

type User struct {
	Name string `json:"name"` //姓名
	Age  int64 `json:"age"`   //年龄
}

json包的marshal,unmarshal

先将结构体序列化成[]byte数组,再从[]byte数组序列化成结构体。

func main() {
	user := User{Name: "小明", Age: 30}
	b, _ := json.Marshal(&user)
	var userMap map[string]interface{}
	_ = json.Unmarshal(b, &userMap)
	for k, v := range userMap{
		fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
	}
}

如上示例执行输出结果如下:

key:name value:小明 value type:string
key:age value:30 value type:float64

如果我们只关心keyvalue的话没问题,如果我们关注其类型的时候那么问题来了,Age字段结构体中明明定义的是int类型,但输出却是float类型的,百思不得其解,通过查找资料发现问题出在了json里面,那就是Go语言中的json包在序列化空接口存放的数字类型(整型、浮点型等)都会序列化成float64类型。为了解决这个问题,我们就想到了反射。

使用反射

使用通过标签(tag)和反射通过遍历结构体字段的方式生成map,具体代码如下:

func main() {
	user := User{Name: "小明", Age: 30}
	userMap := make(map[string]interface{})
	v := reflect.ValueOf(user)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	t := v.Type()
	// 遍历结构体字段
	// 指定tagName值为map中key;字段值为map中value
	for i := 0; i < v.NumField(); i++ {
		fi := t.Field(i)
		if tagValue := fi.Tag.Get("json"); tagValue != "" {
			userMap[tagValue] = v.Field(i).Interface()
		}
	}
	for k, v := range userMap{
		fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
	}
}

通过执行上述代码输出如下:

key:name value:小明 value type:string
key:age value:30 value type:int64

对,没错,这才是我们想要的结果。

第三方库structs

第三种方法是使用第三方库https://github.com/fatih/structs,不用我们再手动写。

安装:

go get github.com/fatih/structs

使用:

func main() {
	user := User{Name: "小明", Age: 30}
	userMap := structs.Map(user)
	for k, v := range userMap{
		fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
	}
}

输出结果:

key:Name value:小明 value type:string
key:Age value:30 value type:int64

两行代码搞定,6的起飞。

总结

以上就是本文的全部内容了,主要介绍了Go中结构体转为map的实现三种方式,