json 是一种结构化的字符串,因为它的编码和解码都有非常成熟的 library 支持,有些语言甚至已经原生支持编码和解码,比如 Go 就是。另一方面,json 数据有着易读易编写的特性,因此被广泛应用于网络数据的传输。
如果你还不知道 json 为何物,那就先阅读一下相关知识:http://www.w3school.com.cn/json/
1. 将结构体序列化成 json
type Movie struct {
Title string
Year int
Color bool
Actors []string
duration int
}
m := Movie {
Title: "Casablanca",
Year: 1942,
Color: false,
Actors: []string{
"Humphrey Bogart",
"Ingrid Bergman",
},
duration: 1000,
}
看上面的代码,如果要将对象 m 序列化成 json,最后结果应该像这样(这是一段字段串):
{
"Title":"Casablanca",
"Year":1942,
"Color":false,
"Actors":[
"Humphrey Bogart",
"Ingrid Bergman"
],
"duration": 1000
}
Go 提供了 encoding/json 包,这个包里有很多函数都可以将 go 的结构体对象和 json 字符串之间进行互转。
1.1 encoding/json 包
先来看这个函数:
// 将结构体序列化成 json 字符串
func Marshal(v interface{}) ([]byte, error)
注意这里有一个新的类型 interface{}
,这在很久前你就已经见过了,它有点类似 C++ 的抽象类,主要用来定义接口。在这里你暂且理解成如果参数类型是 interface{}
,表示可以接受任何类型的数据,这就也有点像 C/C++ 里的 void*
。
- 例
b, _ := json.Marshal(m)
fmt.Printf("%s\n", b)
运行后输出:
{"Title":"Casablanca","Year":1942,"Color":false,"Actors":["Humphrey Bogart","Ingrid Bergman"]}
不过,似乎上面的输出少了点东西?对,duration
字段好像不见了。这里需要解释一下:go 对未导出的字段,在序列化成 json 的时候会忽略。
在我们的结构体定义中,duration 是小写字母开头的,因此是一个未导出字段,最后序列化的时候就看不到了。
1.2 struct tag
go 允许通过一些用户自定义的 tag 标签,来控制序列化的行为以达到特殊效果。举例说明:
type Movie struct {
Title string `json:"title"`
Year int `json:"release"`
Color bool `json:"color"`
Actors []string `json:"actors"`
duration int
}
重新定义 Movie 结构体后,再次使用 marshal 序列化,看看结果如何。
// 注意这里,使用了另一个新的序列化函数 MarshalIndent,它能使输出的结果更加 Pretty.
b, _ := json.MarshalIndent(m, "", " ")
fmt.Printf("%s\n", b)
结果输出:
{
"title": "Casablanca",
"release": 1942,
"color": false,
"actors": [
"Humphrey Bogart",
"Ingrid Bergman"
]
}
不知道你有没有什么发现?是的,所有字段的名称都变了。tag 的语法非常简单,在字段后后面使用反引号将 key:value
括起来就行了。tag 允许有多个 key:value
,以空格进行分隔。另外,一个 key 也可以有多个 value,以逗号分隔,例如:
Year int `json:year,omitempty xml:release`
如果 key 是 json,第一个 value 表示序列成 json 后字段的新名称,第二个 value 是一个控制选项 omitempty,表示如果当前字段是零值,则序列化成 json 后忽略该字段。
另外当 key 是 xml 时,和 json 类似。比如上面的序列化成 xml 后,字段名称就是 release.
有关 tag 的一些知识我们后面还会接触到,以后遇到其它的再继续说明。这里有一篇官方文档比较全面,可以参考一下:https://github.com/golang/go/wiki/Well-known-struct-tags
2. 将 json 反序列化成结构体对象
// 将 json 字符串反序列化成结构体
func Unmarshal(data []byte, v interface{}) error
这个函数也很简单,来个例子就明白了:
var m Movie
// 注意这里,字符串也可以使用反引号。不使用双引号是因为 json 串里已经包含了双引号
b := `{"title":"Casablanca","release":1942,"color":false,"actors":["Humphrey Bogart","Ingrid Bergman"]}`
json.Unmarshal([]byte(b), &m)
fmt.Printf("%#v\n", m)
图1 反序列化
3. 总结
- 掌握结构体和 json 字符串间的序列化和反序列化
- 掌握 struct tag
练习:查阅一下 encoding/json 包里还有其它哪些函数。