使用 Mapstructure 解析 Json,你学会了吗?

背景前几天群里的小伙伴问了一个这样的问题:

使用 Mapstructure 解析 Json,你学会了吗?

文章插图
图片
其实质就是在面对 value 类型不确定的情况下,怎么解析这个 json?
我下意识就想到了 [mapstructure](https://Github.com/mitchellh/mapstructure) 这个库,它可以帮助我们类似 php 那样去处理弱类型的结构 。
介绍先来介绍一下 mapstructure 这个库主要用来做什么的吧,官网是这么介绍的:
mapstructure 是一个 Go 库,用于将通用映射值解码为结构,反之亦然,同时提供有用的错误处理 。
该库在解码数据流(JSON、Gob 等)中的值时最为有用 , 因为在读取部分数据之前,您并不十分清楚底层数据的结构 。因此 , 您可以读取 map[string]interface{} 并使用此库将其解码为适当的本地 Go 底层结构 。
简单来说 , 它擅长解析一些我们并不十分清楚底层数据结构的数据流到我们定义的结构体中 。
下面我们通过几个例子来简单介绍一下 mapstructure 怎么使用 。
例子普通形式func normalDecode() { type Person struct {NamestringAgeintEmAIls []stringExtramap[string]string } // 此输入可以来自任何地方,但通常来自诸如解码 JSON 之类的东西,我们最初不太确定结构 。input := map[string]interface{}{"name":"Tim","age":31,"emails": []string{"one@gmail.com", "two@gmail.com", "three@gmail.com"},"extra": map[string]string{"Twitter": "Tim",}, } var result Person err := mapstructure.Decode(input, &result) if err != nil {panic(err) } fmt.Printf("%#vn", result)}输出:
main.Person{Name:"Tim", Age:31, Emails:[]string{"one@gmail.com", "two@gmail.com", "three@gmail.com"}, Extra:map[string]string{"twitter":"Tim"}}这个方式应该是我们最经常使用的,非常简单的将 map[string]interface{} 映射到我们的结构体中 。
在这里,我们并没有指定每个 field 的 tag , 让 mapstructure 自动去映射 。
如果我们的 input 是一个 json 字符串,那么我们需要将 json 字符串解析为 map[string]interface{} 之后 , 再将其映射到我们的结构体中 。
func jsonDecode() { var jsonStr = `{ "name": "Tim", "age": 31, "gender": "male"}` type Person struct {NamestringAgeintGender string } m := make(map[string]interface{}) err := json.Unmarshal([]byte(jsonStr), &m) if err != nil {panic(err) } var result Person err = mapstructure.Decode(m, &result) if err != nil {panic(err.Error()) } fmt.Printf("%#vn", result)}输出:
main.Person{Name:"Tim", Age:31, Gender:"male"}嵌入式结构mapstructure 允许我们压缩多个嵌入式结构,并通过 squash 标签进行处理 。
func embeddedStructDecode() { // 使用 squash 标签允许压缩多个嵌入式结构 。通过创建多种类型的复合结构并对其进行解码来演示此功能 。type Family struct {LastName string } type Location struct {City string } type Person struct {Family`mapstructure:",squash"`Location`mapstructure:",squash"`FirstName string } input := map[string]interface{}{"FirstName": "Tim","LastName":"Liu","City":"China, Guangdong", } var result Person err := mapstructure.Decode(input, &result) if err != nil {panic(err) } fmt.Printf("%s %s, %sn", result.FirstName, result.LastName, result.City)}输出:
Tim Liu, China, Guangdong在这个例子中, Person 里面有着 Location 和 Family 的嵌入式结构体,通过 squash 标签进行压缩 , 从而达到平铺的作用 。
元数据func metadataDecode() { type Person struct {NamestringAgeintGender string } // 此输入可以来自任何地方 , 但通常来自诸如解码 JSON 之类的东西,我们最初不太确定结构 。input := map[string]interface{}{"name":"Tim","age":31,"email": "one@gmail.com", } // 对于元数据 , 我们制作了一个更高级的 DecoderConfig,以便我们可以更细致地配置所使用的解码器 。在这种情况下,我们只是告诉解码器我们想要跟踪元数据 。var md mapstructure.Metadata var result Person config := &mapstructure.DecoderConfig{Metadata: &md,Result:&result, } decoder, err := mapstructure.NewDecoder(config) if err != nil {panic(err) } if err = decoder.Decode(input); err != nil {panic(err) } fmt.Printf("value: %#v, keys: %#v, Unused keys: %#v, Unset keys: %#vn", result, md.Keys, md.Unused, md.Unset)}


推荐阅读