Golang中结构体映射mapstructure库深入详解
作者:alwaysrun 发布时间:2024-04-26 17:34:40
在数据传递时,需要先编解码;常用的方式是JSON编解码(参见《golang之JSON处理》)。但有时却需要读取部分字段后,才能知道具体类型,此时就可借助mapstructure库了。
mapstructure库
mapstructure可方便地实现map[string]interface{}
与struct
间的转换;使用前,需要先导入库:
go get github.com/mitchellh/mapstructure
字段标签
默认情况下,mapstructure使用字段的名称做匹配映射(即在map中以字段名为键值查找字段值);注意匹配时是忽略大小写的。也可通过标签来设定字段映射名称:
type Person struct {
Name string `mapstructure:"userName"`
}
内嵌结构
go中结构体是可以任意嵌套的;嵌套后即认为拥有对应的字段。但是,默认情况下mapstructure只处理当前结构定义的字段,若要自动处理内嵌字段需要添加标签squash
:
type Student struct {
Person `mapstructure:",squash"`
Age int
}
未映射字段
若源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。可以在结构体中定义一个特殊字段(类型为map[string]interface{}
,且标签要设置为mapstructure:",remain"
),来存放所有未能映射的字段中。
type Student struct {
Name string
Age int
Other map[string]interface{} `mapstructure:",remain"`
}
Metadata
mapstructure中可以使用Metadata收集一些解码时会产生的有用信息。
// mapstructure.go
type Metadata struct {
Keys []string // 解码成功的键
Unused []string // 源数据中存在,但目标结构中不存在的键
Unset []string // 未设定的(源数据中缺失的)键
}
为了获取这些信息,需要使用DecodeMetadata来解码:
var metadata mapstructure.Metadata
err := mapstructure.DecodeMetadata(m, &p, &metadata)
弱类型输入
有时候,并不想对结构体字段类型和map[string]interface{}
的对应键值做强类型一致的校验。这时可以使用WeakDecode/WeakDecodeMetadata方法,它们会尝试做类型转换:
布尔转字符串:true = “1”, false = “0”;
布尔转数字:true = 1, false = 0;
数字转布尔:true if value != 0;
字符串转布尔:可接受,
真:1, t, T, TRUE, true, True
假:0, f, F, FALSE, false, False
数字转字符串:自动base10转换;
负数转为无符号数(上溢);
字符串转数字:根据前缀(如0x等)转换;
空数组与空map间互转;
单个值转为切片;
逆向转换
除将map转换为结构体外,mapstructure也可以将结构体反向解码为map[string]interface{}
。在反向解码时,我们可以为某些字段设置mapstructure:“,omitempty”,当这些字段为默认值时,就不会出现在map中:
p := &Student{
Name: "Mike",
Age: 12,
}
var m map[string]interface{}
mapstructure.Decode(p, &m)
解码器
mapstructure提供了解码器(Decoder),可灵活方便地控制解码:
type DecoderConfig struct {
// 若设定,则在任何解码或类型转换(设定了WeaklyTypedInput)前调用;对于设定了squash的内嵌字段,整体调用一次;若返回错误,则整个解码失败
DecodeHook DecodeHookFunc
// 若设定,则源数据中存在未使用字段时,报错
ErrorUnused bool
// 若设定,则有字段未设定时,报错
ErrorUnset bool
// 若设定,则在设定字段前先清空(对于map等类型会先清理掉旧数据)
ZeroFields bool
// 若设定,支持若类型间的转换
WeaklyTypedInput bool
// Squash will squash embedded structs.
Squash bool
// Metadata is the struct that will contain extra metadata about
// the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata
// Result is a pointer to the struct that will contain the decoded
// value.
Result interface{}
// The tag name that mapstructure reads for field names. This
// defaults to "mapstructure"
TagName string
// IgnoreUntaggedFields ignores all struct fields without explicit
// TagName, comparable to `mapstructure:"-"` as default behaviour.
IgnoreUntaggedFields bool
// MatchName is the function used to match the map key to the struct
// field name or tag. Defaults to `strings.EqualFold`. This can be used
// to implement case-sensitive tag values, support snake casing, etc.
MatchName func(mapKey, fieldName string) bool
}
一个支持弱类型转换的示例:要获取的结果放到config的result中
Name string
Age int
}
func decoderConfig() {
m := map[string]interface{}{
"name": 123,
"age": "12",
"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 {
log.Printf("Result: %#v", p)
log.Printf("keys:%#v, unused:%#v\n", metadata.Keys, metadata.Unused)
} else {
log.Println("decode fail:", err)
}
}
示例
通过一个messageData结构,action会指示最终的data类型。接收到数据后,先解析出atcion,再根据action转换为真实的类型。
因time.Time是一个结构体(json序列化时会转换为时间字符串),mapstructure无法正确处理,所以推荐使用时间戳。
为了能正确解析内嵌的DataBasic,需要标记为squash。
import "github.com/mitchellh/mapstructure"
type DataBasic struct {
DataId string `json:"dataId"`
UpdateTime int64 `json:"updateTime"`
}
type AddedData struct {
DataBasic `mapstructure:",squash"`
Tag string `json:"tag"`
AddParams map[string]any `json:"addParams"`
}
type messageData struct {
Action int `json:"action"`
SeqId uint64 `json:"seqId"`
Data any `json:"data"`
}
func decodeData() {
add := &AddedData{
DataBasic: DataBasic{
DataId: "a2",
UpdateTime: time.Now().UnixMilli(),
},
Tag: "tag",
AddParams: map[string]any{"dataId": "c2", "otherId": "t2"},
}
data := &messageData{
Action: 1,
Data: add,
}
js, err := json.Marshal(data)
if err != nil {
log.Printf("marshal fail: %v", err)
return
}
got := &messageData{}
err = json.Unmarshal(js, got)
if err != nil {
log.Printf("unmarshal fail: %v", err)
return
}
param := new(AddedData)
err = mapstructure.Decode(got.Data, param)
if err != nil {
log.Printf("unmarshal fail: %v", err)
return
}
log.Printf("param: %+v", param)
}
来源:https://blog.csdn.net/alwaysrun/article/details/128516027
猜你喜欢
- swagger介绍Swagger本质上是一种用于描述使用JSON表示的RESTful API的接口描述语言。Swagger与一组开源软件工具
- 从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数。可是,在协程中, yield 通常出现在表达式的右边(例如, da
- 导读:《我不是药神》是由文牧野执导,徐峥、王传君、周一围、谭卓、章宇、杨新鸣等主演的喜剧电影,于 2018 年 7 月 6 日在中国上映。影
- 本文实例讲述了Python中文竖排显示的方法。分享给大家供大家参考。具体如下:这里将中文竖排显示比如 衣食者人之生利也,然且犹尚有节,葬埋者
- atan()方法返回x的反正切值,以弧度表示。Syntax以下是atan()方法的语法:atan(x)注意:此函数是无法直接访问
- 简介模拟登录淘宝已经不是一件新鲜的事情了,过去我曾经使用get/post方式进行爬虫,同时也加入IP代理池进行跳过检验,但随着大型网站的升级
- 问题描述本人pycharm使用anaconda创建的虚拟环境后,使用pycharm终端安装第三方库,但路径一直安装到磁盘下的系统路径中,如图
- 总的感觉,python本身并没有对二进制进行支持,不过提供了一个模块来弥补,就是struct模块。python没有二进制类型,但可以存储二进
- 我们将看到Sigls(变量名称开头处的符号)Perl 5和Perl 6之间的差别。概述让我们从Perl 5和Perl 6中的Sigils概述
- 面向对象设计与面向对象编程的关系 面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD 可以由纯结构化语言来实现,比
- 能坚持全部做完的都是高手直入主题建库建表插入数据代码直接按顺序复制就可以-- 建库CREATE DATABASE `emp`;-- 打开库U
- 测试通过: ie6 + opera + FF + chrome if (document.all) { window.attachEvent
- 本文实例讲述了Python基类函数的重载与调用方法。分享给大家供大家参考。具体分析如下:刚接触Python语言的时间不长,对于这个语言的很多
- 在上一篇文章中实现了树莓派下对摄像头的调用,有兴趣的可以看一下:python+opencv实现摄像头调用的方法接下来,我们将使用python
- 具体方法:1使用panda read_excel 方法加载excel2使用concat将DataFrame列表进行拼接3然后使用pd.Exc
- PHP中重定向网页跳转页面的方法(共三种)第一种:利用header()函数进行重定向,这也是我用的较多的。(注意!locationhe和“:
- 1 create table test(coltest varchar(20))2 实现这一功能 的 sql 语句 s
- 前言CORS 即 Cross Origin Resource Sharing 跨域资源共享.跨域请求分两种:简单请求、复杂请求.简单请求简单
- 今天在做一个老项目时,遇到一个需求,在javascript将url中的参数加密解密,从网上找发现了这段有用的代码:<SCRIPT LA
- 列表(List)是你使用Python过程中接触最为频繁的数据结构,也是功能最为强大的几种数据结构之一。Python列表非常的万能且蕴含着许多