目录
mapstructure库字段标签内嵌结构未映射字段Metadata弱类型输入逆向转换解码器示例在数据传递时,需要先编解码;常用的方式是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方法,它们会尝试做类型转换:
逆向转换
除将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) }
到此这篇关于Golang中结构体映射mapstructure库深入详解的文章就介绍到这了,更多相关Go mapstructure内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
关键词:
上一篇:今日热搜:Go singleflight使用以及原理
下一篇:最后一页
X 关闭
X 关闭
- 15G资费不大降!三大运营商谁提供的5G网速最快?中国信通院给出答案
- 2联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 3亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 4现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 5如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 6AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 7转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 8充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 9好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 10名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?