首页 > 其他分享 >Golang中结构体映射mapstructure库深入详解

Golang中结构体映射mapstructure库深入详解

时间:2023-07-27 16:05:01浏览次数:391  
标签:map string err Golang json 详解 mapstructure struct

mapstructure用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断   +

目录

在数据传递时,需要先编解码;常用的方式是JSON编解码(参见《golang之JSON处理》)。但有时却需要读取部分字段后,才能知道具体类型,此时就可借助mapstructure库了。

mapstructure库

mapstructure可方便地实现map[string]interface{}struct间的转换;使用前,需要先导入库:

go get github.com/mitchellh/mapstructure

字段标签

默认情况下,mapstructure使用字段的名称做匹配映射(即在map中以字段名为键值查找字段值);注意匹配时是忽略大小写的。也可通过标签来设定字段映射名称:

1 2 3 type Person struct {   Name string `mapstructure:"userName"` }

内嵌结构

go中结构体是可以任意嵌套的;嵌套后即认为拥有对应的字段。但是,默认情况下mapstructure只处理当前结构定义的字段,若要自动处理内嵌字段需要添加标签squash

1 2 3 4 type Student struct {   Person `mapstructure:",squash"`   Age int }

未映射字段

若源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。可以在结构体中定义一个特殊字段(类型为map[string]interface{},且标签要设置为mapstructure:",remain"),来存放所有未能映射的字段中。

1 2 3 4 5 type Student struct {   Name  string   Age   int   Other map[string]interface{} `mapstructure:",remain"` }

Metadata

mapstructure中可以使用Metadata收集一些解码时会产生的有用信息。

1 2 3 4 5 6 // 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中:

1 2 3 4 5 6 p := &Student{   Name: "Mike",   Age:  12, } var m map[string]interface{} mapstructure.Decode(p, &m)

解码器

mapstructure提供了解码器(Decoder),可灵活方便地控制解码:

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 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中

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     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。

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 41 42 43 44 45 46 47 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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

标签:map,string,err,Golang,json,详解,mapstructure,struct
From: https://www.cnblogs.com/ExMan/p/17585179.html

相关文章

  • C#中(&&,||)与(&,|)的区别详解
    对于(&&,||),运算的对象是逻辑值,也就是True/False&&相当与中文的并且,||相当于中文的或者。(叫做逻辑运算符又叫短路运算符)运算结果只有下列四种情况。True&&True=True(左边为true,再验证右边也为true,返回结果true)假如这是一个查询条件,则执行。True&&False=False......
  • Kafka集群数据同步 MirrorMaker详解
    1、什么是MirrorMaker?MirrorMaker是Kafka附带的一个用于在Kafka集群之间制作镜像数据的工具。该工具主要作用是从源集群中消费并生产到目标集群。一个集群可以启动多个MirrorMaker配置到多个Kafka集群;2、如果想在Kafka测试集群,同步线的Kafka数据,做测试使用1、在测试集群配置mi......
  • Java方法详解
    Java方法详解什么是方法-System.out.println(),那么她是什么?-Java方法是语句的集合,它们在一起执行一个功能1方法是解决一类问题的步骤的有序组合2方法包含于类或对象中3方法在程序中被创建,在其他地方被引用-设计方法的原则:方法的本意是功能......
  • GDB详解
    GDB调试启动gdb调试的方法一般有三种方式:gdbfilenamegdbattachpidgdbfilenamecorename方法一直接调试目标程序gdbfilenamefilename就是需要启动调试的程序文件名,直接gdb启动一个程序进行调试,也就是说这个程序还没有启动。方法二附加进程gdbattachpid一个程......
  • java线程详解
    java线程详解线程概念说到线程,就不得不提进程,为什么呢,因为进程是操作系统进行分配资源和调度的最小单位,比如windows系统安装的应用软件(office、qq、微信等)启动时,由操作系统协调分配资源和调度执行称之为一个进程,进程间是相互独立和隔离的。而线程是进程最小执行单位,一个进程的......
  • 定位详解
    定位这个东西,blablabla,什么absolute,fixed...一头雾水定位=定位模式+边偏移定位模式......
  • 详解!视频直播源码平台搭建开发:录制功能
     在互联网快速发展的现代社会,直播成为了新型的社交媒体形式与营销形式,普通用户会在视频直播源码平台中发布短视频、图文或是开直播等进行社交;商户则会发布视频广告或直播带货等进行营销。而在这些社交与营销的新形式下,大多数人还会发布直播的某些有趣或意义的片段到短视频上,进行......
  • Vue详解----一篇带你从头领悟到尾,享受飞升的感觉
    ......
  • positional encoding位置编码详解:绝对位置与相对位置编码对比
    目录前言WhyWhat绝对位置编码相对位置编码SinusoidalPositionEncodingComplexembeddingHow前言相信熟悉BERT的小伙伴对positionalencoding(位置表示)肯定都不会陌生~虽然positionalencoding只是BERT中比较小的一个组成部分,但是实际上却暗藏玄机。所以,今天呢我们就把positional......
  • Java 基础 - 泛型机制详解
    著作权归@pdai所有原文链接:https://pdai.tech/md/java/basic/java-basic-x-generic.html Java泛型这个特性是从JDK1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(TypeErasure),将......