Go 每日一库之 mapstructure_dz45693的博客-CSDN博客_mapstructure


本站和网页 https://blog.csdn.net/ma_jiang/article/details/120720840 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

Go 每日一库之 mapstructure_dz45693的博客-CSDN博客_mapstructure
Go 每日一库之 mapstructure
dz45693
于 2021-10-12 11:54:35 发布
3069
收藏
分类专栏:
GO
文章标签:
golang
restful
http
原文链接:https://zhuanlan.zhihu.com/p/165419292
版权
GO
专栏收录该内容
110 篇文章
8 订阅
订阅专栏
简介
mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断。这时,我们可以先使用标准的encoding/json库将数据解码为map[string]interface{}类型,然后根据标识字段利用mapstructure库转为相应的 Go 结构体以便使用。
快速使用
本文代码采用 Go Modules。
首先创建目录并初始化:
$ mkdir mapstructure && cd mapstructure
$ go mod init github.com/darjun/go-daily-lib/mapstructure
下载mapstructure库:
$ go get github.com/mitchellh/mapstructure
使用:
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)
运行结果:
$ go run main.go
person {dj 18 programmer}
cat {kitty 1 Ragdoll}
我们定义了两个结构体Person和Cat,他们的字段有些许不同。现在,我们约定通信的 JSON 串中有一个type字段。当type的值为person时,该 JSON 串表示的是Person类型的数据。当type的值为cat时,该 JSON 串表示的是Cat类型的数据。
上面代码中,我们先用json.Unmarshal将字节流解码为map[string]interface{}类型。然后读取里面的type字段。根据type字段的值,再使用mapstructure.Decode将该 JSON 串分别解码为Person和Cat类型的值,并输出。
实际上,Google Protobuf 通常也使用这种方式。在协议中添加消息 ID 或全限定消息名。接收方收到数据后,先读取协议 ID 或全限定消息名。然后调用 Protobuf 的解码方法将其解码为对应的Message结构。从这个角度来看,mapstructure也可以用于网络消息解码,如果你不考虑性能的话 。
字段标签
默认情况下,mapstructure使用结构体中字段的名称做这个映射,例如我们的结构体有一个Name字段,mapstructure解码时会在map[string]interface{}中查找键名name。注意,这里的name是大小写不敏感的!
type Person struct {
Name string
当然,我们也可以指定映射的字段名。为了做到这一点,我们需要为字段设置mapstructure标签。例如下面使用username代替上例中的name:
type Person struct {
Name string `mapstructure:"username"`
看示例:
type Person struct {
Name string `mapstructure:"username"`
Age int
Job string
type Cat struct {
Name string
Age int
Breed string
func main() {
datas := []string{`
"type": "person",
"username":"dj",
"age":18,
"job": "programmer"
`,
`
"type": "cat",
"name": "kitty",
"Age": 1,
"breed": "Ragdoll"
`,
`
"type": "cat",
"Name": "rooooose",
"age": 2,
"breed": "shorthair"
`,
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)
上面代码中,我们使用标签mapstructure:"username"将Person的Name字段映射为username,在 JSON 串中我们需要设置username才能正确解析。另外,注意到,我们将第二个 JSON 串中的Age和第三个 JSON 串中的Name首字母大写了,但是并没有影响解码结果。mapstructure处理字段映射是大小写不敏感的。
内嵌结构
结构体可以任意嵌套,嵌套的结构被认为是拥有该结构体名字的另一个字段。例如,下面两种Friend的定义方式对于mapstructure是一样的:
type Person struct {
Name string
// 方式一
type Friend struct {
Person
// 方式二
type Friend struct {
Person Person
为了正确解码,Person结构的数据要在person键下:
map[string]interface{} {
"person": map[string]interface{}{"name": "dj"},
我们也可以设置mapstructure:",squash"将该结构体的字段提到父结构中:
type Friend struct {
Person `mapstructure:",squash"`
这样只需要这样的 JSON 串,无效嵌套person键:
map[string]interface{}{
"name": "dj",
看示例:
type Person struct {
Name string
type Friend1 struct {
Person
type Friend2 struct {
Person `mapstructure:",squash"`
func main() {
datas := []string{`
"type": "friend1",
"person": {
"name":"dj"
`,
`
"type": "friend2",
"name": "dj2"
`,
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 "friend1":
var f1 Friend1
mapstructure.Decode(m, &f1)
fmt.Println("friend1", f1)
case "friend2":
var f2 Friend2
mapstructure.Decode(m, &f2)
fmt.Println("friend2", f2)
注意对比Friend1和Friend2使用的 JSON 串的不同。
另外需要注意一点,如果父结构体中有同名的字段,那么mapstructure会将JSON 中对应的值同时设置到这两个字段中,即这两个字段有相同的值。
未映射的值
如果源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。
我们可以在结构体中定义一个字段,为其设置mapstructure:",remain"标签。这样未映射的值就会添加到这个字段中。注意,这个字段的类型只能为map[string]interface{}或map[interface{}]interface{}。
看示例:
type Person struct {
Name string
Age int
Job string
Other map[string]interface{} `mapstructure:",remain"`
func main() {
data := `
"name": "dj",
"age":18,
"job":"programmer",
"height":"1.8m",
"handsome": true
`
var m map[string]interface{}
err := json.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatal(err)
var p Person
mapstructure.Decode(m, &p)
fmt.Println("other", p.Other)
上面代码中,我们为结构体定义了一个Other字段,用于保存未映射的键值。输出结果:
other map[handsome:true height:1.8m]
逆向转换
前面我们都是将map[string]interface{}解码到 Go 结构体中。mapstructure当然也可以将 Go 结构体反向解码为map[string]interface{}。在反向解码时,我们可以为某些字段设置mapstructure:",omitempty"。这样当这些字段为默认值时,就不会出现在结构的map[string]interface{}中:
type Person struct {
Name string
Age int
Job string `mapstructure:",omitempty"`
func main() {
p := &Person{
Name: "dj",
Age: 18,
var m map[string]interface{}
mapstructure.Decode(p, &m)
data, _ := json.Marshal(m)
fmt.Println(string(data))
上面代码中,我们为Job字段设置了mapstructure:",omitempty",且对象p的Job字段未设置。运行结果:
$ go run main.go
{"Age":18,"Name":"dj"}
Metadata
解码时会产生一些有用的信息,mapstructure可以使用Metadata收集这些信息。Metadata结构如下:
// mapstructure.go
type Metadata struct {
Keys []string
Unused []string
Metadata只有两个导出字段:
Keys:解码成功的键名;Unused:在源数据中存在,但是目标结构中不存在的键名。
为了收集这些数据,我们需要使用DecodeMetadata来代替Decode方法:
type Person struct {
Name string
Age int
func main() {
m := map[string]interface{}{
"name": "dj",
"age": 18,
"job": "programmer",
var p Person
var metadata mapstructure.Metadata
mapstructure.DecodeMetadata(m, &p, &metadata)
fmt.Printf("keys:%#v unused:%#v\n", metadata.Keys, metadata.Unused)
先定义一个Metadata结构,传入DecodeMetadata收集解码的信息。运行结果:
$ go run main.go
keys:[]string{"Name", "Age"} unused:[]string{"job"}
错误处理
mapstructure执行转换的过程中不可避免地会产生错误,例如 JSON 中某个键的类型与对应 Go 结构体中的字段类型不一致。Decode/DecodeMetadata会返回这些错误:
type Person struct {
Name string
Age int
Emails []string
func main() {
m := map[string]interface{}{
"name": 123,
"age": "bad value",
"emails": []int{1, 2, 3},
var p Person
err := mapstructure.Decode(m, &p)
if err != nil {
fmt.Println(err.Error())
上面代码中,结构体中Person中字段Name为string类型,但输入中name为int类型;字段Age为int类型,但输入中age为string类型;字段Emails为[]string类型,但输入中emails为[]int类型。故Decode返回错误。运行结果:
$ go run main.go
5 error(s) decoding:
* 'Age' expected type 'int', got unconvertible type 'string'
* 'Emails[0]' expected type 'string', got unconvertible type 'int'
* 'Emails[1]' expected type 'string', got unconvertible type 'int'
* 'Emails[2]' expected type 'string', got unconvertible type 'int'
* 'Name' expected type 'string', got unconvertible type 'int'
从错误信息中很容易看出哪里出错了。
弱类型输入
有时候,我们并不想对结构体字段类型和map[string]interface{}的对应键值做强类型一致的校验。这时可以使用WeakDecode/WeakDecodeMetadata方法,它们会尝试做类型转换:
type Person struct {
Name string
Age int
Emails []string
func main() {
m := map[string]interface{}{
"name": 123,
"age": "18",
"emails": []int{1, 2, 3},
var p Person
err := mapstructure.WeakDecode(m, &p)
if err == nil {
fmt.Println("person:", p)
} else {
fmt.Println(err.Error())
虽然键name对应的值123是int类型,但是在WeakDecode中会将其转换为string类型以匹配Person.Name字段的类型。同样的,age的值"18"是string类型,在WeakDecode中会将其转换为int类型以匹配Person.Age字段的类型。 需要注意一点,如果类型转换失败了,WeakDecode同样会返回错误。例如将上例中的age设置为"bad value",它就不能转为int类型,故而返回错误。
解码器
除了上面介绍的方法外,mapstructure还提供了更灵活的解码器(Decoder)。可以通过配置DecoderConfig实现上面介绍的任何功能:
// mapstructure.go
type DecoderConfig struct {
ErrorUnused bool
ZeroFields bool
WeaklyTypedInput bool
Metadata *Metadata
Result interface{}
TagName string
各个字段含义如下:
ErrorUnused:为true时,如果输入中的键值没有与之对应的字段就返回错误;ZeroFields:为true时,在Decode前清空目标map。为false时,则执行的是map的合并。用在struct到map的转换中;WeaklyTypedInput:实现WeakDecode/WeakDecodeMetadata的功能;Metadata:不为nil时,收集Metadata数据;Result:为结果对象,在map到struct的转换中,Result为struct类型。在struct到map的转换中,Result为map类型;TagName:默认使用mapstructure作为结构体的标签名,可以通过该字段设置。
看示例:
type Person struct {
Name string
Age int
func main() {
m := map[string]interface{}{
"name": 123,
"age": "18",
"job": "programmer",
var p Person
var metadata mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
WeaklyTypedInput: true,
Result: &p,
Metadata: &metadata,
})
if err != nil {
log.Fatal(err)
err = decoder.Decode(m)
if err == nil {
fmt.Println("person:", p)
fmt.Printf("keys:%#v, unused:%#v\n", metadata.Keys, metadata.Unused)
} else {
fmt.Println(err.Error())
这里用Decoder的方式实现了前面弱类型输入小节中的示例代码。实际上WeakDecode内部就是通过这种方式实现的,下面是WeakDecode的源码:
// mapstructure.go
func WeakDecode(input, output interface{}) error {
config := &DecoderConfig{
Metadata: nil,
Result: output,
WeaklyTypedInput: true,
decoder, err := NewDecoder(config)
if err != nil {
return err
return decoder.Decode(input)
再实际上,Decode/DecodeMetadata/WeakDecodeMetadata内部都是先设置DecoderConfig的对应字段,然后创建Decoder对象,最后调用其Decode方法实现的。
总结
mapstructure实现优雅,功能丰富,代码结构清晰,非常推荐一看!
大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue
dz45693
关注
关注
点赞
收藏
评论
Go 每日一库之 mapstructure
简介mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断。这时,我们可以先使用标准的encoding/json库将数据解码为map[string]interface{}类型,然后根据标识字段利用mapstructure库转为相应的 Go 结构体以便使用。快速使用本文代码采用 Go Modules。首先创建
复制链接
扫一扫
专栏目录
Go-mapstructure-Go库用于解码泛型map值成Go结构体
08-13
mapstructure - Go库用于解码泛型map值成Go结构体
Golang库解析之mapstructure
HJ
05-12
1103
mapstructure传送门
package main
import (
"encoding/json"
"fmt"
"github.com/mitchellh/mapstructure"
type Demo struct {
ID int `json:"id"`
Name string `json:"name"`
func main() {
var value = make(map[string]Demo)
value["1"] = Demo{
ID: 1,
参与评论
您还未登录,请先
登录
后发表或查看评论
mapstructure:Go库,用于将通用地图值解码为本地Go结构,反之亦然
03-31
地图结构
mapstructure是一个Go库,用于将通用地图值解码为结构,反之亦然,同时提供有用的错误处理。
从一些数据流(JSON,采空区等),您不太了解底层数据的结构,直到你读它的一部分解码值时,该库是最有用的。 因此,您可以读取map[string]interface{}并使用此库将其解码为适当的基础本机Go结构。
安装
标准go get :
$ go get github.com/mitchellh/mapstructure
用法与范例
有关用法和示例,请参见 。
Decode功能具有与之相关的示例。
但为什么?!
Go提供了出色的标准库来解码JSON等格式。 标准方法是预先创建一个结构,然后从编码格式的字节中填充该结构。 很好,但是问题是您的配置或编码是否随特定字段而略有变化。 例如,考虑以下JSON:
" type " : " person " ,
golang常用库之mapstructure包 | 多json格式情况解析、GO json 如何转化为 map 和 struct、Go语言结构体标签(Struct Tag)
最新发布
西京刀客
10-01
524
Go library for decoding generic map values into native Go structures and vice versa.
go语言map转struct,使用mitchellh/mapstructure,mapstructure.Decode遇到的坑
热门推荐
梅雨心情的博客
11-30
1万+
Golang中的map转strcut
在go语言的项目中,map与struct相互转换的需求很常见。网上有一些简单粗暴的解决方法,利用json的序列化和反序列化进行map与struct的转换,但是当map或struct有比较复杂的复合结构时,这种方法会忽略复杂结构的字段。所以这里介绍一种比较好的转换方式。
使用mapstructure.Decode
先直接上代码吧!
type params str...
go语言map转结构体
每天学习一点点的博客
08-21
9248
方式一、通过mapstructure.Decode()方法
可以通过mapstructure.Decode(map[string]interface,*struct)方法将map转换成结构体,如:
type student struct{
golang mapstructure 转出为空的问题
仅做个人笔记
09-07
1485
//struct映射
func (s *commentRedisStore) CommentMapToStruct(commentInfo *model.CommentInfo, mapValue map[string]string) error {
if len(mapValue) == 0 {
return errors.New("len(mapValue) == 0")
d...
Go 语言 mapstructure 使用
王小明的专栏
11-13
1307
参考资料
https://pkg.go.dev/github.com/mitchellh/mapstructure
https://segmentfault.com/a/1190000023442894
Go:mapstructure包的使用
了凡的博客
03-26
8257
文章目录1、导包2、为什么3、Decode:map转换成结构体1、不支持内部结构体转换2、常规转换3、结论4、DecodePath:复制内部某个结构体
1、导包
如果是Idea的话,在我们的Terminal上面输入命令,进行包的获取:
go get github.com/goinggo/mapstructure
正常是不会有问题,如果下载不下来报permission denie可以看一下我的...
go包 interface解析到结构体github.com/mitchellh/mapstructure
qq_40530622的博客
08-05
241
package main
import (
"fmt"
"github.com/mitchellh/mapstructure"
// go get github.com/mitchellh/mapstructure
// 将interface解析到结构体上
func main() {
InterfaceToStruct()
Squash()
TypeErr()
Remain()
type Person struct {
Name string
Age int
Go源码分析:mapstructure
了凡的博客
01-27
1388
mapstructure源码阅读
map转结构体
map切片转结构体切片
map部分转换
------------------分享使人进步
Go 每日一库之 goquery
dz45693的专栏
10-12
2042
简介
goquery是用 Go 语言编写的一个类似于 jQuery 的库。它基于 HTML 解析库net/html和 CSS 库cascadia,提供与 jQuery 相近的接口。Go 著名的爬虫框架colly就是基于 goquery 的。
快速使用
本文代码使用 Go Modules。
创建目录并初始化:
$ mkdir goquery && cd goquery
$ go mod init github.com/darjun/go-daily-lib/goquery
安装g
Go 如何解析 json 内部结构不确定的情况
Seekload
10-18
3228
本文是通过组织曾经梳理过的一篇文章和一个问答而成。主要介绍的是关于 Go 如何解析 json 内部结构不确定的情况。问题描述这或许是新手常会遇到的一个问题,无论是在各种微...
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
©️2022 CSDN
皮肤主题:编程工作室
设计师:CSDN官方博客
返回首页
dz45693
CSDN认证博客专家
CSDN认证企业博客
码龄16年
暂无认证
576
原创
1万+
周排名
6万+
总排名
620万+
访问
等级
6万+
积分
689
粉丝
535
获赞
377
评论
1017
收藏
私信
关注
热门文章
Windows Server 2003/2008 密码 无法更新密码。为新密码提供的值不符合字符域的长度、复杂性或历史要求。
50098
C#将WebBowser控件替换为Chrome内核
29757
ASP.Net获得新浪天气预报几种方式总结
26844
InstallShield 2015 Limited Edition 打包程序详解
25111
你应该学会的接口调试神器——Postman高级用法
24844
分类专栏
区块链
jenkins
1篇
TeamCity
ElasticSearch
8篇
HubbleDotNet
2篇
ASP.NET
103篇
ASP.NET MVc
63篇
C#.NET基础
184篇
C#多线程编程
31篇
CLR与.NET基类库
17篇
Entity Framework
8篇
IIS 与HTTP
17篇
javascript和jQuery
72篇
MSSql2005及新版本
61篇
MSSQl数据库
30篇
Office
21篇
SharePoint
71篇
SQL索引
10篇
TeamCity,jenkins
26篇
VS2010及新版本
34篇
Windows Server
35篇
XML
11篇
地图
8篇
正则表达式
13篇
程序人生
32篇
计算机技术与基础
53篇
C#集合
13篇
HTML
25篇
PostSharp
11篇
Memcached redis
24篇
服务器负载均衡
3篇
HubbleDotNet,ElasticSearch
4篇
测试
10篇
php
8篇
Integration Service
11篇
Lync
5篇
ExChange
8篇
故障转移集群
4篇
MQ
9篇
MySQL
21篇
ASP.NET Core
49篇
Unity3D
zookeeper
5篇
python
36篇
mongodb
4篇
Java
4篇
react vue webpack
24篇
GO
110篇
docker k8s
54篇
Linux
9篇
inflxudb
1篇
运维
8篇
最新评论
Autofac 批量注入
奋斗中年人:
先获取到所有类库,然后写个循环,一个类库一个类库找,然后注入更舒服。
Redis Scan 原理解析与踩坑
TheFeasterfromAfar:
大佬牛
k8s集群日志收集ELK和Graylog
ch-大花:
是不是没有创建pvc 或者没有创建sc
asp.net 5.0 https的双向认证(windows和ubuntu) 以及go的调用
lilinhao6:
大佬你好,请问.net程序 根据您的示例写的 现在有个问题就是怎么验证客户端证书和服务端证书来自同一个根证书
Windows Server 2003/2008 密码 无法更新密码。为新密码提供的值不符合字符域的长度、复杂性或历史要求。
llllllllGDan:
咋解决的老哥
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
玩转 Go 生态|Hertz WebSocket 扩展简析
[Go疑难杂症]为什么nil不等于nil
线上服务宕机,码农试用期被毕业,原因竟是给MySQL加个字段
2022年32篇
2021年83篇
2020年55篇
2019年88篇
2018年113篇
2017年93篇
2016年100篇
2015年36篇
2014年83篇
2013年78篇
2012年161篇
2011年151篇
2010年203篇
2009年115篇
2007年1篇
目录
目录
分类专栏
区块链
jenkins
1篇
TeamCity
ElasticSearch
8篇
HubbleDotNet
2篇
ASP.NET
103篇
ASP.NET MVc
63篇
C#.NET基础
184篇
C#多线程编程
31篇
CLR与.NET基类库
17篇
Entity Framework
8篇
IIS 与HTTP
17篇
javascript和jQuery
72篇
MSSql2005及新版本
61篇
MSSQl数据库
30篇
Office
21篇
SharePoint
71篇
SQL索引
10篇
TeamCity,jenkins
26篇
VS2010及新版本
34篇
Windows Server
35篇
XML
11篇
地图
8篇
正则表达式
13篇
程序人生
32篇
计算机技术与基础
53篇
C#集合
13篇
HTML
25篇
PostSharp
11篇
Memcached redis
24篇
服务器负载均衡
3篇
HubbleDotNet,ElasticSearch
4篇
测试
10篇
php
8篇
Integration Service
11篇
Lync
5篇
ExChange
8篇
故障转移集群
4篇
MQ
9篇
MySQL
21篇
ASP.NET Core
49篇
Unity3D
zookeeper
5篇
python
36篇
mongodb
4篇
Java
4篇
react vue webpack
24篇
GO
110篇
docker k8s
54篇
Linux
9篇
inflxudb
1篇
运维
8篇
目录
评论
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。
余额充值