ch4-5: review

This commit is contained in:
chai2010 2016-01-08 13:34:55 +08:00
parent 0e9ae4ab00
commit 42502b5aa2

View File

@ -1,12 +1,12 @@
## 4.5. JSON ## 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編碼而不是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類型是字符串和結構體。例如
@ -20,7 +20,7 @@ object {"year": 1980,
"medals": ["gold", "silver", "bronze"]} "medals": ["gold", "silver", "bronze"]}
``` ```
考慮一個應用程序該程序負責收集各種電影評論併提供反饋功能。它的Movie數據類型和一個典型的表示電影的值列表如下所示。其中結構體聲明中Year和Color成員後面的字符串面值是結構體成員Tag我們稍後會解釋它的作用。 考慮一個應用程序該程序負責收集各種電影評論併提供反饋功能。它的Movie數據類型和一個典型的表示電影的值列表如下所示。結構體聲明中Year和Color成員後面的字符串面值是結構體成員Tag我們稍後會解釋它的作用。
```Go ```Go
gopl.io/ch4/movie gopl.io/ch4/movie
@ -53,7 +53,7 @@ if err != nil {
fmt.Printf("%s\n", data) fmt.Printf("%s\n", data)
``` ```
Marshal函數生成一個字節slice包含很長的字符串併且沒有空白縮進我們將它摺行以便於顯示 Marshal函數返還一個編碼後的字節slice包含很長的字符串併且沒有空白縮進我們將它摺行以便於顯示
``` ```
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr [{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
@ -62,7 +62,7 @@ tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
Actors":["Steve McQueen","Jacqueline Bisset"]}] Actors":["Steve McQueen","Jacqueline Bisset"]}]
``` ```
這種緊湊的表示形式雖然包含了全部的信息但是很難閲讀。爲了生成便於閲讀的格式另一個json.MarshalIndent函數將産生整齊縮進的輸出。有兩個額外的字符串參數用於表示每一行輸出的前綴和每一個層級的縮進 這種緊湊的表示形式雖然包含了全部的信息但是很難閲讀。爲了生成便於閲讀的格式另一個json.MarshalIndent函數將産生整齊縮進的輸出。該函數有兩個額外的字符串參數用於表示每一行輸出的前綴和每一個層級的縮進:
```Go ```Go
data, err := json.MarshalIndent(movies, "", " ") data, err := json.MarshalIndent(movies, "", " ")
@ -72,7 +72,7 @@ if err != nil {
fmt.Printf("%s\n", data) fmt.Printf("%s\n", data)
``` ```
上面的代碼將産生這樣的輸出: 上面的代碼將産生這樣的輸出(譯註:在最後一個成員或元素後面併沒有逗號分隔符)
```Json ```Json
[ [
@ -106,16 +106,16 @@ 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"` Year int `json:"released"`
Color bool `json:"color,omitempty"` 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 ```Go
var titles []struct{ Title string } var titles []struct{ Title string }
@ -125,7 +125,7 @@ if err := json.Unmarshal(data, &titles); err != nil {
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]" fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
``` ```
許多web服務都提供JSON接口通過HTTP接口發送JSON格式請求併返迴JSON格式的信息。爲了説明這一點我們通過Github的issue査詢服務。首先我們要定義合適的類型和常量 許多web服務都提供JSON接口通過HTTP接口發送JSON格式請求併返迴JSON格式的信息。爲了説明這一點我們通過Github的issue査詢服務來演示類似的用法。首先,我們要定義合適的類型和常量:
```Go ```Go
gopl.io/ch4/github gopl.io/ch4/github
@ -199,7 +199,7 @@ 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但是在下一節我們將看到如果利用模闆來輸出複雜的格式。
@ -260,5 +260,3 @@ GitHub的Web服務接口 https://developer.github.com/v3/ 包含了更多的特
**練習 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工具通過命令行輸入的電影名字下載對應的海報。