golang常用库之mapstructure包 | 多json格式情况解析、GO json 如何转化为 map 和 struct、Go语言结构体标签(Struct Tag)

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.Unmarshalmsgpack.Unmarshal将字节流解码为map[string]interface{}类型。然后读取里面的type字段。根据type字段的值,再使用mapstructure.Decode将该 JSON 串分别解码为不同的struct。

实际上,Google Protobuf 通常也使用这种方式。在协议中添加消息 ID 或全限定消息名。接收方收到数据后,先读取协议 ID 或全限定消息名。然后调用 Protobuf 的解码方法将其解码为对应的Message结构。

四、mapstructure基础

mapstructure GitHub:https://github.com/mitchellh/mapstructure

1、Go语言结构体标签(Struct Tag)

Go语言之旅:Struct Tag的介绍及用法
参考URL: https://cloud.tencent.com/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.com/mitchellh/mapstructure

demo:多json格式情况解析,根据json type字段,映射到不同的struct

网上demo

package main
import (
  "encoding/json"
  "fmt"
  "log"
  "github.com/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://www.cnblogs.com/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.com/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.com/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.com/developer/article/1476724

猜你喜欢

转载自blog.csdn.net/inthat/article/details/127121728