文章目录
- golang常用库之mitchellh/mapstructure包 | go将map转换为struct
- 一、msgpack
- 二、背景
- 三、多json格式情况解析使用思路
- 四、mapstructure基础
- 1、Go语言结构体标签(Struct Tag)
- mapstructure 字段标签
- 2、map转结构体-通过mapstructure.Decode()方法
- map转结构体注意的点
- 3、逆向转换-结构体转map
- 4、Metadata
- 五、代码demo
- demo:多json格式情况解析,根据json type字段,映射到不同的struct
- 【推荐】demo:GO json 如何转化为 map 和 struct
- mapstructure.Decode方法将map转换成结构体
- Viper使用mitchellh/mapstructure
- 参考
golang常用库之mitchellh/mapstructure包 | go将map转换为struct
一、msgpack
msgpack用起来像json,但是却比json快,并且序列化以后的数据长度更小,言外之意,使用msgpack不仅序列化和反序列化的速度快,数据传输量也比json格式小,msgpack同样支持多种语言。
二、背景
很多时候,解析来自多数据源头的数据流时,我们事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断。这时,我们可以先使用标准的encoding/json
库将数据解码为map[string]interface{}
类型,然后根据标识字段利用mapstructure库转为相应的 Go 结构体以便使用。
在日常开发中,我们经常遇到这样一个问题,就是要反序列化其他地方传递来的json,因为数据结构未知,所以我们便会使
用map[string]interface{}来接收反序列化的结果。
工作中其实比较常用~
三、多json格式情况解析使用思路
mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。
当我们的数据源是多种json数据格式时,我们需要读取到一些字段之后才能做出判断。 这时,我们可以约定通信的 JSON 串中有一个type字段,根据这个type字段确定我们具体的json格式时哪个。
先用json.Unmarshal
或msgpack.Unmarshal
将字节流解码为map[string]interface{}类型。然后读取里面的type字段。根据type字段的值,再使用mapstructure.Decode将该 JSON 串分别解码为不同的struct。
实际上,Google Protobuf 通常也使用这种方式。在协议中添加消息 ID 或全限定消息名。接收方收到数据后,先读取协议 ID 或全限定消息名。然后调用 Protobuf 的解码方法将其解码为对应的Message结构。
四、mapstructure基础
mapstructure GitHub:https://github/mitchellh/mapstructure
mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。
1、Go语言结构体标签(Struct Tag)
Go语言之旅:Struct Tag的介绍及用法
参考URL: https://cloud.tencent/developer/article/1496468
结构体标签是对结构体字段的额外信息标签。
Struct Tag的组成部分
Struct Tag是存在于Struct下面成员的附加属性,它的定义永远都是以key-value的形式出现的,多个定义的情况下以空格分割。
在StructTag的应用中,使用最多的就是json的序列化了,Json字符串转结构体对象,主要利用的就是结构体对应的字段tag,json解析的原理就是通过反射获得每个字段的tag,然后把解析的json对应的值赋给他们。
package main
import (
"fmt"
"reflect"
)
func main() {
// 使用reflect.StructTag解析这段文本的tag内容
tag := reflect.StructTag(`json:"foo,omitempty" xml:"foo"`)
// 直接使用Get获取json定义
value := tag.Get("json")
fmt.Printf("value: %q\n", value)
}
mapstructure 字段标签
默认情况下,mapstructure使用结构体中字段的名称做这个映射,例如我们的结构体有一个Name字段,mapstructure解码时会在map[string]interface{}中查找键名name。注意,这里的name是大小写不敏感的!
type Person struct {
Name string
}
当然,我们也可以指定映射的字段名。为了做到这一点,我们需要为字段设置mapstructure标签。例如下面使用username代替上例中的name:
type Person struct {
Name string `mapstructure:"username"`
}
2、map转结构体-通过mapstructure.Decode()方法
用 mapstructure 包,所以 struct tag 标识不再是 json,而是 mapstructure。
该方法的参数有两个,第一个参数是要转换的map变量,第二个参数是struct结构体变量指针。
map转结构体注意的点
1、map[string]interface的键值将对应字段赋值到结构体时忽略大小写;
2、结构体中所有字段名必须以大写字母开头,否则将无法赋值
3、map 通常有几个不足:
通过 key 获取数据,可能出现不存在的 key,为了严谨,需要检查 key 是否存在;
相对于结构体的方式,map数据提取不便且不能利用 IDE 补全检查,key 容易写错;
4、如果源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。
5、
3、逆向转换-结构体转map
mapstructure当然也可以将 Go 结构体反向解码为map[string]interface{}。在反向解码时,我们可以为某些字段设置mapstructure:“,omitempty”。这样当这些字段为默认值时,就不会出现在结构的map[string]interface{}中。
4、Metadata
解码时会产生一些有用的信息,mapstructure可以使用Metadata收集这些信息。Metadata结构如下:
// mapstructure.go
type Metadata struct {
Keys []string
Unused []string
}
Metadata只有两个导出字段:
- Keys:解码成功的键名;
- Unused:在源数据中存在,但是目标结构中不存在的键名。
五、代码demo
mapstructure GitHub:https://github/mitchellh/mapstructure
demo:多json格式情况解析,根据json type字段,映射到不同的struct
网上demo
package main
import (
"encoding/json"
"fmt"
"log"
"github/mitchellh/mapstructure"
)
type Person struct {
Name string
Age int
Job string
}
type Cat struct {
Name string
Age int
Breed string
}
func main() {
datas := []string{`
{
"type": "person",
"name":"dj",
"age":18,
"job": "programmer"
}
`,
`
{
"type": "cat",
"name": "kitty",
"age": 1,
"breed": "Ragdoll"
}
`,
}
for _, data := range datas {
var m map[string]interface{}
err := json.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatal(err)
}
switch m["type"].(string) {
case "person":
var p Person
mapstructure.Decode(m, &p)
fmt.Println("person", p)
case "cat":
var cat Cat
mapstructure.Decode(m, &cat)
fmt.Println("cat", cat)
}
}
}
【推荐】demo:GO json 如何转化为 map 和 struct
GO json 如何转化为 map 和 struct
参考URL: https://wwwblogs/akidongzi/p/12036096.html
推荐阅读原篇,写的比较好,比较实用~
mapstructure.Decode方法将map转换成结构体
mapstructure.Decode(map[string]interface,*struct)方法将map转换成结构体,该方法的参数有两个,
第一个参数是要转换的map变量,
第二个参数是struct结构体变量指针,
网上demo:
“”"
注意的点:
1、map[string]interface的键值将对应字段赋值到结构体时忽略大小写;
2、结构体中所有字段名必须以大写字母开头,否则将无法赋值
3、使用mapstructure.Decode()方法不能转化携带_特殊符号的变量
“”"
package main
import (
"fmt"
"github/mitchellh/mapstructure"
)
type student struct{
id int `json:"id"`
Name string `json:"name"`
Adress []string `json:"adress"`
}
func main() {
val := map[string]interface{}{
"id":1,
"name":"xiaoming",
"adress":[]string{"beijing","nanjing"},
}
stu := student{}
err := mapstructure.Decode(val,&stu)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println("val:")
fmt.Println(val)
fmt.Println("struct:")
fmt.Println(stu)
}
Viper使用mitchellh/mapstructure
Viper 在后台使用github/mitchellh/mapstructure来解析值,其默认情况下使用mapstructure tags。当我们需要将 Viper 读取的配置反序列到我们定义的结构体变量中时,一定要使用 mapstructure tags。
参考
Go 每日一库之 mapstructure
参考URL: https://darjun.github.io/2020/07/29/godailylib/mapstructure/
GO小知识之实例演示 json 如何转化为 map 和 struct
参考URL: https://cloud.tencent/developer/article/1476724
更多推荐
golang常用库之mapstructure包 | 多json格式情况解析、GO json 如何转化为 map 和 struct、Go语言结构体标签(Struc
发布评论