Go 语言 mapstructure 使用

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

前言

我们经常遇到如何将 map[string]interface{} 转化为 struct, 这个过程会用到反射, 通过反射可以实现,不确定的成员依然适用 map[string]interface{} 表示,确定结构后,再将 map[string]interface{} 解析为具体的某个结构。

主要使用的是 mapstructure 来实现,将 map 转换称 struct

一个第三方库,地址:github.com/mitchellh/m…

json 转换成 map ,然后 map 转换成 struct

json 转换成 struct

json 转换成 struct 只需要使用 json.unmashal 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
go复制代码type User struct {
Name string
FansCount int64
}
func TestJsonUnmarshal(t *testing.T) {
const jsonStream = `
{"name":"ethancai", "fansCount": 9223372036854775807}
`
var user User // 类型为User
err := JsonUnmarshal(jsonStream, &user)
if err != nil {
fmt.Println("error:", err)
}

fmt.Printf("%+v \n", user)
}

// 反json化, jsonStr是json化的字符串,v传空的结构体
func JsonUnmarshal(jsonStr string, v interface{}) error {
var fasterJson = jsoniter.ConfigCompatibleWithStandardLibrary
byteValue := []byte(jsonStr)
err := fasterJson.Unmarshal(byteValue, v)
if err != nil {
logs.Error("JsonUnmarshal failed for v: %+v, err = %s", v, err.Error())
return errors.New("JsonUnmarshal failed for v: %+v")
}
return nil
}

map 转换成 struct

代码原理: json.Unmarshal 将字节流解码为map[string]interface{}类型, 然后使用mapstructure.Decode将该 JSON 串分别解码为对象值。

下载包:

1
arduino复制代码go get https://github.com/mitchellh/mapstructure

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
go复制代码package jsontool

import (
"encoding/json"
"fmt"
"testing"

"github.com/mitchellh/mapstructure"
)

type Blog struct {
BlogId string `mapstructure:"blogId"`
Title string `mapstructrue:"title"`
Content string `mapstructure:"content"`
Uid string `mapstructure:"uid"`
State string `mapstructure:"state"`
}

type Event struct {
Type string `json:"type"`
Database string `json:"database"`
Table string `json:"table"`
Data []map[string]string `json:"data"`
}

func TestMapStructure(t *testing.T) {
e := Event{}
msg := []byte(`{ "type": "UPDATE", "database": "blog", "table": "blog", "data": [ { "blogId": "100001", "title": "title", "content": "this is a blog", "uid": "1000012", "state": "1" } ]}`)
if err := json.Unmarshal(msg, &e); err != nil {
panic(err)
}
if e.Table == "blog" {
var blogs []Blog
if err := mapstructure.Decode(e.Data, &blogs); err != nil {
panic(err)
}
fmt.Println(blogs)
}

}

执行结果:

1
2
3
4
kotlin复制代码=== RUN   TestMapStructure
[{100001 title this is a blog 1000012 1}]
--- PASS: TestMapStructure (0.00s)
PASS

其中 msg 数据结构如下:
在这里插入图片描述

字段标签 mapstructure

默认情况下 mapstructure 使用结构体中字段的名称做映射,例如结构体中有一个 Title 字段, mapstructure 解码时会在 map[string]interface{}中查找键名title,并且字段不区分大小写。

也可以指定字段,设置 mapstructure 标签

1
2
3
4
5
6
7
go复制代码type Blog struct {
BlogId string `mapstructure:"blogId"`
Title string `mapstructrue:"title"`
Content string `mapstructure:"content"`
Uid string `mapstructure:"uid"`
State string `mapstructure:"state"`
}

jpath 标签

通过jpath标签指明字段对应的map中实际的值

比如 有个字段Age,需要从map的birth字段转换过来,那么我的map和字段处理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
go复制代码//map的结构:
{
"people":{
"age":{
"birth":"10", //这个是需要转换成Age的字段
"birthDay":"2021-1-27"
}
}
}

type example struct{
Age `jpath:"age.birth"`
}

Metadata

mapstructure 执行转换的过程中会可能会一些错误,这个错误主要记录在 mapstructure.Metadata

结构如下:

1
2
3
4
5
go复制代码// mapstructure.go
type Metadata struct {
Keys []string
Unused []string
}
  • Keys:解码成功的键名;
  • Unused:在源数据中存在,但是目标结构中不存在的键名。

测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
go复制代码var document3 = `{"categories":["12","222","333"],"people":{"name":"jack","age":{"birth":10,"year":2000,"animals":[{"barks":"yes","tail":"yes"},{"barks":"no","tail":"yes"}]}}}`

type Items1 struct {
Categories []string `mapstructure:"Categories"`
Peoples People1 `mapstructure:"People"` // Specify the location of the array
}

type People1 struct {
Age int `mapstructure:"age.birth"` // jpath is relative to the array
Animals []Animal `mapstructure:"age.animals"`
}
type Animal struct {
Barks string `jpath:"barks"`
}

func TestMetaData(t *testing.T) {
var items1 Items1
config := &mapstructure.DecoderConfig{
Metadata: &mapstructure.Metadata{
Keys: nil,
Unused: nil,
},
Result: &items1,
}
decoder, _ := mapstructure.NewDecoder(config)
docScript := []byte(document3)
var docMap map[string]interface{}
_ = json.Unmarshal(docScript, &docMap)

_ = decoder.Decode(docMap)
fmt.Println(config.Metadata.Keys)
fmt.Println(config.Metadata.Unused)
}

执行结果:

1
2
3
4
5
css复制代码=== RUN   TestMetaData
[Categories[0] Categories[1] Categories[2] Categories People]
[People.age People.name]
--- PASS: TestMetaData (0.00s)
PASS

欢迎关注公众号:程序员财富自由之路

参考资料

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%