>

##### 简介 ---- JSON 作为一种网络传输格式非常广泛,各种语言几乎都有支持,Go 语言也一样,提供了一个标准库 `encoding/json` 用于处理这种格式的数据。

首先,我们必须明白,JSON 文本(或者称为 JSON 对象)本质上就是一串符合一定格式的字符串(在 Go 中是 []byte 来表示)。Go 的标准库 encoding/json 提供了 Marshal 和 Unmarshal 两个核心函数,
其中 Marshal 用于把 Go 语言中的一个对象(比如 struct 或者其他合法的 Go 数据类型)转换成 json 字符串(一串 []byte 数组),而 Unmarshal 则是用于把 json 字符串转换为 Go 语言中的对象。

在使用 Unmarshal 时,只有符合 json 格式的 json 数据才能被成功的识别为 json,否则会提示格式错误。


##### Marshal ---- Marshal 函数只有在编码成功的时候才会返回数据,而且 JSON 只支持 string 作为 key。所以,如果要编码 Go 程序中的 map 类型的变量,那么该变量必须是 map[string]T 这种类型(T 是 Go 语言中任意的合法类型)

除此之外,还需要注意以下例外情况:

  • channel, complex 和 function 是不能被编码成 JSON 的
  • 嵌套的数据是不能编码的,不然会让 JSON 编码进入死循环
  • 指针在编码的时候会输出指针指向的内容,而空指针则会输出 null

在声明一个 struct 变量时,如果这个变量在以后的使用过程中需要进行 Marshal 操作的话,struct 的成员声明需要符合一定的规则,如下:

  • 必须大写,如果 field 没有定义为大写(导出)的话,encoding/json 包将无法访问该 struct 的私有成员,从而导致无法 Marshal。
  • 由于 struct 成员大写,而 json 对象的 key 一般(非强制)是小写。所以,如果希望生成的 json 对象的 key 是小写,可以使用反引号(即 back-tik ``) 作为 tag 来装饰 struct 的成员 field。

装饰方式见如下的例子:

1
2
3
4
5
6
7
type Page struct {
Id int64 `json:"-"`
Title string `json:"page,omitempty"`
Comments []string `json:"comments"`
Reads int `json:"reads,string"`
Version string `json:"version" toml:"version"`
}

相关语法解释:

  • json:"-" 表示这个字段不输出到 JSON 中
  • 其中 omitempty 的意思是,如果该成员变量的值是空的话,就不 Marshal 该变量,也即生成的 json 对象中没有该变量。
  • tag 中带有自定义名称,那么这个自定义名称会出现在 JSON 的字段名中,例如上面例子中
  • 如果字段类型是 bool, string, int, int64 等,而 tag 中带有 “, string” 选项,那么这个字段在输出到 JSON 的时候会把该字段对应的值转换成 JSON 字符串
  • 可以定义多个输出类型,比如同时定义 json 和 toml 格式的输出字段类型。

函数原型:

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

Marshal 用法示例:
1) array/slice

1
2
slice := []string{"apple", "peach", "pear"}
js, _ := json.Marshal(slice)

2) map

1
2
3
slice := []
mapD := map[string]int{"apple": 5, "lettuce": 7}
js, _ := json.Marshal(mapD)

3) struct

1
2
3
4
5
resp := &Response{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}
}
js, _ := json.Marshal(resp)

如何打印 json 数据
在 Go 中, json 对象是由一串 []byte 字节数组定义的,需要先把 js 转化成 string 再进行打印

1
fmt.Println("result:", string(js))

构造 json 数据
如上所述,json 对象在 Go 中是一串 []byte 数组(string literal)。

1
js := []byte(`{"num":6.13,"strs":["a","b"]}`)   // 此处构造了一个内部是 map 类型的 json 对象

##### Unmarshal ----

函数原型

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

Umarshal 时,用什么数据结构去接收解码后的js son对象

Unmarshal 时可以用对应的具体数据类型去接收,也可以用 interface{} 去接收。
如果用 interface{} 去接收的话,那么 Unmarshal 出来的结果会是 map[string]interface{}。
而且要注意,如果 json 中的数据是 int 等数字类型的话,用 interface{} 接收时会 float 类型,这点要格外注意。

Unmarshal 用法示例

1) map

1
2
3
4
5
var dat map[string]interface{}
if err := json.Unmarshal(js, &dat); err != nil{
panic(err)
}
fmt.Println(dat)

2) struct

1
2
3
js := `{"page": 1, "fruits": ["apple", "peach"]}`
res := Response{}
json.Unmarshal([]byte(str), &res)

提示:如果 struct 定义的字段与 json 数据的字段不匹配,那么只有 struct 中定义了的字段会被附上值,其余的字段依然会是默认值。

3) interface{}

1
2
3
4
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

var f interface{}
err := json.Unmarshal(b, &f)

这里 Unmarshal 得到的 f 实际上是一个 map[string]interface{} 类型, 如下:

1
2
3
4
5
6
7
8
9
10
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}

m := f.(map[string]interface{})

得到这个 map 之后,我们通常需要遍历 map 来提取我们需要的具体内容:
官方提供了一个如下的一个方式(断言)来取出这个 map 中的各个 key value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}

由上可见,这种操作方式非常不方便,尤其是对于较复杂的或者嵌套层次较多的对象来说,所幸的是,网上有许多开源的解决方案帮助我们更好的处理这种位置结构体的 JSON。

比如: bitly 公司开源 simplejson 包, 还有 ffjson 等。

使用 simplejson 来解码 json 对象的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
js, err := NewJson([]byte(`{
"test": {
"array": [1, "2", 3],
"int": 10,
"float": 5.150,
"bignum": 9223372036854775807,
"string": "simplejson",
"bool": true
}
}`))

arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()



参考:
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/07.2.md
https://kev.inburke.com/kevin/golang-json-http/