mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-17 19:24:19 +08:00
回到简体
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
## 4.5. JSON
|
||||
|
||||
JavaScript對象表示法(JSON)是一種用於發送和接收結構化信息的標準協議。在類似的協議中,JSON併不是唯一的一個標準協議。 XML(§7.14)、ASN.1和Google的Protocol Buffers都是類似的協議,併且有各自的特色,但是由於簡潔性、可讀性和流行程度等原因,JSON是應用最廣泛的一個。
|
||||
JavaScript对象表示法(JSON)是一种用于发送和接收结构化信息的标准协议。在类似的协议中,JSON并不是唯一的一个标准协议。 XML(§7.14)、ASN.1和Google的Protocol Buffers都是类似的协议,并且有各自的特色,但是由于简洁性、可读性和流行程度等原因,JSON是应用最广泛的一个。
|
||||
|
||||
Go語言對於這些標準格式的編碼和解碼都有良好的支持,由標準庫中的encoding/json、encoding/xml、encoding/asn1等包提供支持(譯註:Protocol Buffers的支持由 github.com/golang/protobuf 包提供),併且這類包都有着相似的API接口。本節,我們將對重要的encoding/json包的用法做個概述。
|
||||
Go语言对于这些标准格式的编码和解码都有良好的支持,由标准库中的encoding/json、encoding/xml、encoding/asn1等包提供支持(译注:Protocol Buffers的支持由 github.com/golang/protobuf 包提供),并且这类包都有着相似的API接口。本节,我们将对重要的encoding/json包的用法做个概述。
|
||||
|
||||
JSON是對JavaScript中各種類型的值——字符串、數字、布爾值和對象——Unicode本文編碼。它可以用有效可讀的方式表示第三章的基礎數據類型和本章的數組、slice、結構體和map等聚合數據類型。
|
||||
JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码。它可以用有效可读的方式表示第三章的基础数据类型和本章的数组、slice、结构体和map等聚合数据类型。
|
||||
|
||||
基本的JSON類型有數字(十進製或科學記數法)、布爾值(true或false)、字符串,其中字符串是以雙引號包含的Unicode字符序列,支持和Go語言類似的反斜槓轉義特性,不過JSON使用的是\Uhhhh轉義數字來表示一個UTF-16編碼(譯註:UTF-16和UTF-8一樣是一種變長的編碼,有些Unicode碼點較大的字符需要用4個字節表示;而且UTF-16還有大端和小端的問題),而不是Go語言的rune類型。
|
||||
基本的JSON类型有数字(十进制或科学记数法)、布尔值(true或false)、字符串,其中字符串是以双引号包含的Unicode字符序列,支持和Go语言类似的反斜杠转义特性,不过JSON使用的是\Uhhhh转义数字来表示一个UTF-16编码(译注:UTF-16和UTF-8一样是一种变长的编码,有些Unicode码点较大的字符需要用4个字节表示;而且UTF-16还有大端和小端的问题),而不是Go语言的rune类型。
|
||||
|
||||
這些基礎類型可以通過JSON的數組和對象類型進行遞歸組合。一個JSON數組是一個有序的值序列,寫在一個方括號中併以逗號分隔;一個JSON數組可以用於編碼Go語言的數組和slice。一個JSON對象是一個字符串到值的映射,寫成以繫列的name:value對形式,用花括號包含併以逗號分隔;JSON的對象類型可以用於編碼Go語言的map類型(key類型是字符串)和結構體。例如:
|
||||
这些基础类型可以通过JSON的数组和对象类型进行递归组合。一个JSON数组是一个有序的值序列,写在一个方括号中并以逗号分隔;一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射,写成以系列的name:value对形式,用花括号包含并以逗号分隔;JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串)和结构体。例如:
|
||||
|
||||
```
|
||||
boolean true
|
||||
@@ -20,7 +20,7 @@ object {"year": 1980,
|
||||
"medals": ["gold", "silver", "bronze"]}
|
||||
```
|
||||
|
||||
考慮一個應用程序,該程序負責收集各種電影評論併提供反饋功能。它的Movie數據類型和一個典型的表示電影的值列表如下所示。(在結構體聲明中,Year和Color成員後面的字符串面值是結構體成員Tag;我們稍後會解釋它的作用。)
|
||||
考虑一个应用程序,该程序负责收集各种电影评论并提供反馈功能。它的Movie数据类型和一个典型的表示电影的值列表如下所示。(在结构体声明中,Year和Color成员后面的字符串面值是结构体成员Tag;我们稍后会解释它的作用。)
|
||||
|
||||
<u><i>gopl.io/ch4/movie</i></u>
|
||||
```Go
|
||||
@@ -42,7 +42,7 @@ var movies = []Movie{
|
||||
}
|
||||
```
|
||||
|
||||
這樣的數據結構特别適合JSON格式,併且在兩種之間相互轉換也很容易。將一個Go語言中類似movies的結構體slice轉爲JSON的過程叫編組(marshaling)。編組通過調用json.Marshal函數完成:
|
||||
这样的数据结构特别适合JSON格式,并且在两种之间相互转换也很容易。将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用json.Marshal函数完成:
|
||||
|
||||
```Go
|
||||
data, err := json.Marshal(movies)
|
||||
@@ -52,7 +52,7 @@ if err != nil {
|
||||
fmt.Printf("%s\n", data)
|
||||
```
|
||||
|
||||
Marshal函數返還一個編碼後的字節slice,包含很長的字符串,併且沒有空白縮進;我們將它摺行以便於顯示:
|
||||
Marshal函数返还一个编码后的字节slice,包含很长的字符串,并且没有空白缩进;我们将它折行以便于显示:
|
||||
|
||||
```
|
||||
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
|
||||
@@ -61,7 +61,7 @@ tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
|
||||
Actors":["Steve McQueen","Jacqueline Bisset"]}]
|
||||
```
|
||||
|
||||
這種緊湊的表示形式雖然包含了全部的信息,但是很難閲讀。爲了生成便於閲讀的格式,另一個json.MarshalIndent函數將産生整齊縮進的輸出。該函數有兩個額外的字符串參數用於表示每一行輸出的前綴和每一個層級的縮進:
|
||||
这种紧凑的表示形式虽然包含了全部的信息,但是很难阅读。为了生成便于阅读的格式,另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进:
|
||||
|
||||
```Go
|
||||
data, err := json.MarshalIndent(movies, "", " ")
|
||||
@@ -71,7 +71,7 @@ if err != nil {
|
||||
fmt.Printf("%s\n", data)
|
||||
```
|
||||
|
||||
上面的代碼將産生這樣的輸出(譯註:在最後一個成員或元素後面併沒有逗號分隔符):
|
||||
上面的代码将产生这样的输出(译注:在最后一个成员或元素后面并没有逗号分隔符):
|
||||
|
||||
```Json
|
||||
[
|
||||
@@ -103,18 +103,18 @@ fmt.Printf("%s\n", data)
|
||||
]
|
||||
```
|
||||
|
||||
在編碼時,默認使用Go語言結構體的成員名字作爲JSON的對象(通過reflect反射技術,我們將在12.6節討論)。隻有導出的結構體成員才會被編碼,這也就是我們爲什麽選擇用大寫字母開頭的成員名稱。
|
||||
在编码时,默认使用Go语言结构体的成员名字作为JSON的对象(通过reflect反射技术,我们将在12.6节讨论)。只有导出的结构体成员才会被编码,这也就是我们为什么选择用大写字母开头的成员名称。
|
||||
|
||||
細心的讀者可能已經註意到,其中Year名字的成員在編碼後變成了released,還有Color成員編碼後變成了小寫字母開頭的color。這是因爲構體成員Tag所導致的。一個構體成員Tag是和在編譯階段關聯到該成員的元信息字符串:
|
||||
细心的读者可能已经注意到,其中Year名字的成员在编码后变成了released,还有Color成员编码后变成了小写字母开头的color。这是因为构体成员Tag所导致的。一个构体成员Tag是和在编译阶段关联到该成员的元信息字符串:
|
||||
|
||||
```
|
||||
Year int `json:"released"`
|
||||
Color bool `json:"color,omitempty"`
|
||||
```
|
||||
|
||||
結構體的成員Tag可以是任意的字符串面值,但是通常是一繫列用空格分隔的key:"value"鍵值對序列;因爲值中含義雙引號字符,因此成員Tag一般用原生字符串面值的形式書寫。json開頭鍵名對應的值用於控製encoding/json包的編碼和解碼的行爲,併且encoding/...下面其它的包也遵循這個約定。成員Tag中json對應值的第一部分用於指定JSON對象的名字,比如將Go語言中的TotalCount成員對應到JSON中的total_count對象。Color成員的Tag還帶了一個額外的omitempty選項,表示當Go語言結構體成員爲空或零值時不生成JSON對象(這里false爲零值)。果然,Casablanca是一個黑白電影,併沒有輸出Color成員。
|
||||
结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:"value"键值对序列;因为值中含义双引号字符,因此成员Tag一般用原生字符串面值的形式书写。json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/...下面其它的包也遵循这个约定。成员Tag中json对应值的第一部分用于指定JSON对象的名字,比如将Go语言中的TotalCount成员对应到JSON中的total_count对象。Color成员的Tag还带了一个额外的omitempty选项,表示当Go语言结构体成员为空或零值时不生成JSON对象(这里false为零值)。果然,Casablanca是一个黑白电影,并没有输出Color成员。
|
||||
|
||||
編碼的逆操作是解碼,對應將JSON數據解碼爲Go語言的數據結構,Go語言中一般叫unmarshaling,通過json.Unmarshal函數完成。下面的代碼將JSON格式的電影數據解碼爲一個結構體slice,結構體中隻有Title成員。通過定義合適的Go語言數據結構,我們可以選擇性地解碼JSON中感興趣的成員。當Unmarshal函數調用返迴,slice將被隻含有Title信息值填充,其它JSON成員將被忽略。
|
||||
编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成。下面的代码将JSON格式的电影数据解码为一个结构体slice,结构体中只有Title成员。通过定义合适的Go语言数据结构,我们可以选择性地解码JSON中感兴趣的成员。当Unmarshal函数调用返回,slice将被只含有Title信息值填充,其它JSON成员将被忽略。
|
||||
|
||||
```Go
|
||||
var titles []struct{ Title string }
|
||||
@@ -124,7 +124,7 @@ if err := json.Unmarshal(data, &titles); err != nil {
|
||||
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
|
||||
```
|
||||
|
||||
許多web服務都提供JSON接口,通過HTTP接口發送JSON格式請求併返迴JSON格式的信息。爲了説明這一點,我們通過Github的issue査詢服務來演示類似的用法。首先,我們要定義合適的類型和常量:
|
||||
许多web服务都提供JSON接口,通过HTTP接口发送JSON格式请求并返回JSON格式的信息。为了说明这一点,我们通过Github的issue查询服务来演示类似的用法。首先,我们要定义合适的类型和常量:
|
||||
|
||||
<u><i>gopl.io/ch4/github</i></u>
|
||||
```Go
|
||||
@@ -157,9 +157,9 @@ type User struct {
|
||||
}
|
||||
```
|
||||
|
||||
和前面一樣,卽使對應的JSON對象名是小寫字母,每個結構體的成員名也是聲明爲大小字母開頭的。因爲有些JSON成員名字和Go結構體成員名字併不相同,因此需要Go語言結構體成員Tag來指定對應的JSON名字。同樣,在解碼的時候也需要做同樣的處理,GitHub服務返迴的信息比我們定義的要多很多。
|
||||
和前面一样,即使对应的JSON对象名是小写字母,每个结构体的成员名也是声明为大小字母开头的。因为有些JSON成员名字和Go结构体成员名字并不相同,因此需要Go语言结构体成员Tag来指定对应的JSON名字。同样,在解码的时候也需要做同样的处理,GitHub服务返回的信息比我们定义的要多很多。
|
||||
|
||||
SearchIssues函數發出一個HTTP請求,然後解碼返迴的JSON格式的結果。因爲用戶提供的査詢條件可能包含類似`?`和`&`之類的特殊字符,爲了避免對URL造成衝突,我們用url.QueryEscape來對査詢中的特殊字符進行轉義操作。
|
||||
SearchIssues函数发出一个HTTP请求,然后解码返回的JSON格式的结果。因为用户提供的查询条件可能包含类似`?`和`&`之类的特殊字符,为了避免对URL造成冲突,我们用url.QueryEscape来对查询中的特殊字符进行转义操作。
|
||||
|
||||
<u><i>gopl.io/ch4/github</i></u>
|
||||
```Go
|
||||
@@ -198,9 +198,9 @@ func SearchIssues(terms []string) (*IssuesSearchResult, error) {
|
||||
}
|
||||
```
|
||||
|
||||
在早些的例子中,我們使用了json.Unmarshal函數來將JSON格式的字符串解碼爲字節slice。但是這個例子中,我們使用了基於流式的解碼器json.Decoder,它可以從一個輸入流解碼JSON數據,盡管這不是必須的。如您所料,還有一個針對輸出流的json.Encoder編碼對象。
|
||||
在早些的例子中,我们使用了json.Unmarshal函数来将JSON格式的字符串解码为字节slice。但是这个例子中,我们使用了基于流式的解码器json.Decoder,它可以从一个输入流解码JSON数据,尽管这不是必须的。如您所料,还有一个针对输出流的json.Encoder编码对象。
|
||||
|
||||
我們調用Decode方法來填充變量。這里有多種方法可以格式化結構。下面是最簡單的一種,以一個固定寬度打印每個issue,但是在下一節我們將看到如果利用模闆來輸出複雜的格式。
|
||||
我们调用Decode方法来填充变量。这里有多种方法可以格式化结构。下面是最简单的一种,以一个固定宽度打印每个issue,但是在下一节我们将看到如果利用模板来输出复杂的格式。
|
||||
|
||||
<u><i>gopl.io/ch4/issues</i></u>
|
||||
```Go
|
||||
@@ -228,7 +228,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
通過命令行參數指定檢索條件。下面的命令是査詢Go語言項目中和JSON解碼相關的問題,還有査詢返迴的結果:
|
||||
通过命令行参数指定检索条件。下面的命令是查询Go语言项目中和JSON解码相关的问题,还有查询返回的结果:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch4/issues
|
||||
@@ -249,12 +249,12 @@ $ ./issues repo:golang/go is:open json decoder
|
||||
#4237 gjemiller encoding/base64: URLEncoding padding is optional
|
||||
```
|
||||
|
||||
GitHub的Web服務接口 https://developer.github.com/v3/ 包含了更多的特性。
|
||||
GitHub的Web服务接口 https://developer.github.com/v3/ 包含了更多的特性。
|
||||
|
||||
**練習 4.10:** 脩改issues程序,根據問題的時間進行分類,比如不到一個月的、不到一年的、超過一年。
|
||||
**练习 4.10:** 修改issues程序,根据问题的时间进行分类,比如不到一个月的、不到一年的、超过一年。
|
||||
|
||||
**練習 4.11:** 編寫一個工具,允許用戶在命令行創建、讀取、更新和關閉GitHub上的issue,當必要的時候自動打開用戶默認的編輯器用於輸入文本信息。
|
||||
**练习 4.11:** 编写一个工具,允许用户在命令行创建、读取、更新和关闭GitHub上的issue,当必要的时候自动打开用户默认的编辑器用于输入文本信息。
|
||||
|
||||
**練習 4.12:** 流行的web漫畵服務xkcd也提供了JSON接口。例如,一個 https://xkcd.com/571/info.0.json 請求將返迴一個很多人喜愛的571編號的詳細描述。下載每個鏈接(隻下載一次)然後創建一個離線索引。編寫一個xkcd工具,使用這些離線索引,打印和命令行輸入的檢索詞相匹配的漫畵的URL。
|
||||
**练习 4.12:** 流行的web漫画服务xkcd也提供了JSON接口。例如,一个 https://xkcd.com/571/info.0.json 请求将返回一个很多人喜爱的571编号的详细描述。下载每个链接(只下载一次)然后创建一个离线索引。编写一个xkcd工具,使用这些离线索引,打印和命令行输入的检索词相匹配的漫画的URL。
|
||||
|
||||
**練習 4.13:** 使用開放電影數據庫的JSON服務接口,允許你檢索和下載 https://omdbapi.com/ 上電影的名字和對應的海報圖像。編寫一個poster工具,通過命令行輸入的電影名字,下載對應的海報。
|
||||
**练习 4.13:** 使用开放电影数据库的JSON服务接口,允许你检索和下载 https://omdbapi.com/ 上电影的名字和对应的海报图像。编写一个poster工具,通过命令行输入的电影名字,下载对应的海报。
|
||||
|
||||
Reference in New Issue
Block a user