介绍

JSON 是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析与生成。其采用的是独立于语言的文本格式。可以去官网 json.org 详细了解。

JSON 中有两种结构,键值对的对象和数组,在 Golang 中对应的结构是struct/map 和 slice。JSON 中的基本类型是 string、number、bool、nul,在 Golang 中对应的就是 string、float64、boolean、nil。

编码

我们可以使用 Marshal 方法来实现 JSON 编码

1
func Marshal(v interface{})([]byte,error)

我们定义一个 People 的结构体:

1
2
3
4
type People struct {
    Name string
    Age int
}

并且实例化:

1
2
3
4
p := People{
    "bob",
    21
}

然后我们就可以使用 json.Marshal 来得到一个该实例的 JSON 编码格式:

1
b,err := json.Marshal(p)

b 就是包含 JSON 数据的字节序列,等价于下边的结果:

[]byte(`{"Name":"bob","Age":21}`)

但是需要注意,并不是结构体中的所有字段都可以编码为 JSON 格式:

  • JSON 只支持 string 作为键,所以 map 中的 key,必须为 string 类型
  • 通道、附属、方法等类型不能被编码
  • 指针类型会对它指向的数据进行编码,如果是 nil,则编码为 null
  • json 包只能获取结构体中导出的字段,因此未导出字段不能被编码

解码

我们可以使用 Unmarshal 方法来解码:

1
func Unmarshal(data []byte,v interface{}) error

我们必须先创建一个变量来存放我们解码之后的数据:

1
var p People

然后调用 json.Unmarshal,并传入一个 JSON 格式的字节序列 和一个指向 p 的指针:

1
err := json.Unmarshal(b,&p)

解码之后,p 中就会存放解码之后的数据,等价于下面的值:

1
2
3
4
People{
    "bob",
    21
}

那 Unmarshal 方法是如何将 JSON 数据中的键跟结构体中的字段一个一个对应起来的呢?我们用 “Name” 这个字段来说明,Unmarshal 会遍历目标结构体的字段,依次按下列顺序优先匹配:

  • 导出字段的 Tag 包含 “Name"的
  • 导出字段的名字是 “Name” 的
  • 导出字段名字不考虑大小写与 “Name” 匹配的

如果找不到匹配的,也就是 JSON数据中的某些键,在结构体中并不存在,则会忽略这些键。比如下面这个例子,将会忽略 love 字段:

1
2
3
b2 := []byte(`{"Name":"bob","love":"coding","Age":21}`)
var p3 People
json.Unmarshal(b2, &p3)

但是还有一个问题,就是如果你不知道结构体的具体类型怎么办?

空接口类型

空接口类型 interface{} 就是一个有零个方法的接口,每一个基本类型都实现了至少 0 个接口,因此每个类型都满足空接口。

空接口可以作为一个通用的容器类型,来存储各种类型的数据:

1
2
3
4
var i interface{}
i = "bob"
i = 88
i = 3.14

通过类型断言,我们可以得到底层具体类型:

1
r:=i.(string) // r 是 string类型

但是,如果我们连底层的具体类型都不知道还怎么断言呢?别慌,我们可以通过 switch 来判断具体类型:

1
2
3
4
5
6
7
switch t := i.(type) {
	case int:
		fmt.Println("string",t*2)
	case string:
		fmt.Println("string",t)
	default:
}

json 包使用了 map[string]interface{} 和 []interface{} 存储任意的 JSON 对象和数组,所以我们可以解析任意的 JSON 数据到一个 空接口 interface{} 类型的值,基本类型对应关系:

  • JSON bool -> bool
  • JSON numbers -> float64
  • JSON string -> string
  • JSON null -> nil

解码任意JSON数据

先定义一个JSON数据:

1
b:=[]byte(`{"Name":"bob","love":"coding","Age":21}`)

假设我们不知道该数据的结构,我们可以先把它解析到空接口值里:

1
2
var f interface{} //此处也可以替换为 map[string]interface{} 类型
err:=json.Unmarshal(b,&f)

此时,f 的值就是一个 map,等价于下面的值

1
2
3
4
5
map[string]interface{}{
"Name":"bob",
"Age":21,
"love":"coding",
}

现在我们知道 f 的底层类型为 map[string]interface{},我们就可以通过断言来获取具体的数据了:

1
2
3
4
5
6
7
8
9
m:=f.(map[string]interface{})
for k,v:=range m{
	switch vv:=v.(type){
		case string:
		fmt.Println("k",k,"string",vv)
		case int:
		fmt.Println("k",k,"int",vv)
    }
}