mapstructure 可用于将通用的map[string]interface{}
解码到对应的 Go 结构体中,或者执行相反的操作。
github 地址: github.com/mitchellh/mapstructure
1. map[string]interface{}到结构体
默认情况下,mapstructure使用字段的名称做匹配映射(即在map中以字段名为键值查找字段值);注意匹配时是忽略大小写的。
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
| package main
import ( "encoding/json" "fmt"
"github.com/mitchellh/mapstructure" )
type Person struct { Name string Age int Job string }
type Cat struct { Name string Age int Breed string }
func main() { str1 := `{"type": "person","name":"dj","age":18,"job": "programmer"}` str2 := `{"type": "cat", "Name": "kitty", "AgE" : 10, "breed": "Ragdoll"}` var map1, map2 map[string]interface{} json.Unmarshal([]byte(str1), &map1) json.Unmarshal([]byte(str2), &map2)
var A Person var B Cat mapstructure.Decode(map1, &A) mapstructure.Decode(map2, &B)
fmt.Printf("A:%#v\n", A) fmt.Printf("B:%#v\n", B) }
|
2.1 字段标签
也可通过标签来设定字段映射名称。
1 2 3
| type Person struct { Name string `mapstructure:"userName"` }
|
2.2 内嵌结构
go中结构体是可以任意嵌套的;嵌套后即认为拥有对应的字段。但是,默认情况下mapstructure只处理当前结构定义的字段,若要自动处理内嵌字段需要添加标签squash
:
1 2 3 4
| type Student struct { Person `mapstructure:",squash"` Age int }
|
2.3 未映射字段
若源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。可以在结构体中定义一个特殊字段(类型为map[string]interface{}
,且标签要设置为mapstructure:",remain"
),来存放所有未能映射的字段中。
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
| package main
import ( "encoding/json" "fmt" "log"
"github.com/mitchellh/mapstructure" )
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) }
|
mapstructure中可以使用Metadata收集一些解码时会产生的有用信息。
1 2 3 4 5 6
| type Metadata struct { Keys []string Unused []string Unset []string }
|
为了获取这些信息,需要使用DecodeMetadata来解码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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) }
|
2.5 弱类型输入
有时候,并不想对结构体字段类型和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间互转;
- 单个值转为切片;
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
| package main
import ( "fmt"
"github.com/mitchellh/mapstructure" )
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()) } }
|
2. 结构体到map[string]interface{}
除将map转换为结构体外,mapstructure也可以将结构体反向解码为map[string]interface{}
。
2.1 最好使用标签
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
| package main
import ( "encoding/json" "fmt"
"github.com/mitchellh/mapstructure" )
type Person struct { NameValue string AgeValue int JobValue string }
func main() { p := &Person{ NameValue: "dj", AgeValue: 18, }
var m map[string]interface{} mapstructure.Decode(p, &m)
fmt.Println("map", m)
data, _ := json.Marshal(m) fmt.Println("str", string(data)) }
type Person struct { NameValue string `mapstructure:"name_value"` AgeValue int `mapstructure:"age_value"` JobValue string `mapstructure:"job_value"` }
|
2.2 默认值不加入到map里
在反向解码时,我们可以为某些字段设置mapstructure:“,omitempty”,当这些字段为默认值时,就不会出现在map中。
1 2 3 4 5 6 7 8 9 10
| type Person struct { NameValue string `mapstructure:"name_value"` AgeValue int `mapstructure:"age_value"` JobValue string `mapstructure:"job_value,omitempty"` }
|
3. 参考资料