mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-25 14:28:58 +00:00
parent
32b21df6ad
commit
691c930e55
150
ch4/ch4-05.md
150
ch4/ch4-05.md
@ -113,4 +113,152 @@ Year int `json:"released"`
|
||||
Color bool `json:"color,omitempty"`
|
||||
```
|
||||
|
||||
TODO
|
||||
結構體的成員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成員將被忽略。
|
||||
|
||||
```Go
|
||||
var titles []struct{ Title string }
|
||||
if err := json.Unmarshal(data, &titles); err != nil {
|
||||
log.Fatalf("JSON unmarshaling failed: %s", err)
|
||||
}
|
||||
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
|
||||
```
|
||||
|
||||
許多web服務都提供JSON接口,通過HTTP接口發送JSON格式請求併返迴JSON格式的信息。爲了説明這一點,我們通過Github的issue査詢服務。首先,我們要定義合適的類型和常量:
|
||||
|
||||
```Go
|
||||
gopl.io/ch4/github
|
||||
// Package github provides a Go API for the GitHub issue tracker.
|
||||
// See https://developer.github.com/v3/search/#search-issues.
|
||||
package github
|
||||
|
||||
import "time"
|
||||
|
||||
const IssuesURL = "https://api.github.com/search/issues"
|
||||
|
||||
type IssuesSearchResult struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []*Issue
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
Number int
|
||||
HTMLURL string `json:"html_url"`
|
||||
Title string
|
||||
State string
|
||||
User *User
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Body string // in Markdown format
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Login string
|
||||
HTMLURL string `json:"html_url"`
|
||||
}
|
||||
```
|
||||
|
||||
和前面一樣,卽使對應的JSON對象名是小寫字母,每個結構體的成員名也是聲明爲大小字母開頭的。因爲有些JSON成員名字和Go結構體成員名字併不相同,因此需要Go語言結構體成員Tag來指定對應的JSON名字。同樣,在解碼的時候也需要做同樣的處理,GitHub服務返迴的信息比我們定義的要多很多。
|
||||
|
||||
SearchIssues函數發出一個HTTP請求,然後解碼返迴的JSON格式的結果。因爲用戶提供的査詢條件可能包含類似`?`和`&`之類的特殊字符,爲了避免對URL造成衝突,我們用url.QueryEscape來對査詢中的特殊字符進行轉義操作。
|
||||
|
||||
```Go
|
||||
gopl.io/ch4/github
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchIssues queries the GitHub issue tracker.
|
||||
func SearchIssues(terms []string) (*IssuesSearchResult, error) {
|
||||
q := url.QueryEscape(strings.Join(terms, " "))
|
||||
resp, err := http.Get(IssuesURL + "?q=" + q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We must close resp.Body on all execution paths.
|
||||
// (Chapter 5 presents 'defer', which makes this simpler.)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("search query failed: %s", resp.Status)
|
||||
}
|
||||
|
||||
var result IssuesSearchResult
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return &result, nil
|
||||
}
|
||||
```
|
||||
|
||||
在早些的例子中,我們使用了json.Unmarshal函數來將JSON格式的字符串解碼爲字節slice。但是這個例子中,我們使用了基於流式的解碼器json.Decoder,它可以從一個流解碼JSON數據,盡管這不是必鬚的。如您所料,還有一個針對輸出流的json.Encoder編碼對象。
|
||||
|
||||
我們調用Decode方法來填充變量。這里有多種方法可以格式化結構。下面是最簡單的一種,以一個固定寬度打印每個issue,但是在下一節我們將看到如果利用模闆來輸出複雜的格式。
|
||||
|
||||
```Go
|
||||
gopl.io/ch4/issues
|
||||
|
||||
// Issues prints a table of GitHub issues matching the search terms.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch4/github"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result, err := github.SearchIssues(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%d issues:\n", result.TotalCount)
|
||||
for _, item := range result.Items {
|
||||
fmt.Printf("#%-5d %9.9s %.55s\n",
|
||||
item.Number, item.User.Login, item.Title)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
通過命令行參數指定檢索條件。下面的命令是査詢Go語言項目中和JSON解碼相關的問題,還有査詢返迴的結果:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch4/issues
|
||||
$ ./issues repo:golang/go is:open json decoder
|
||||
13 issues:
|
||||
#5680 eaigner encoding/json: set key converter on en/decoder
|
||||
#6050 gopherbot encoding/json: provide tokenizer
|
||||
#8658 gopherbot encoding/json: use bufio
|
||||
#8462 kortschak encoding/json: UnmarshalText confuses json.Unmarshal
|
||||
#5901 rsc encoding/json: allow override type marshaling
|
||||
#9812 klauspost encoding/json: string tag not symmetric
|
||||
#7872 extempora encoding/json: Encoder internally buffers full output
|
||||
#9650 cespare encoding/json: Decoding gives errPhase when unmarshalin
|
||||
#6716 gopherbot encoding/json: include field name in unmarshal error me
|
||||
#6901 lukescott encoding/json, encoding/xml: option to treat unknown fi
|
||||
#6384 joeshaw encoding/json: encode precise floating point integers u
|
||||
#6647 btracey x/tools/cmd/godoc: display type kind of each named type
|
||||
#4237 gjemiller encoding/base64: URLEncoding padding is optional
|
||||
```
|
||||
|
||||
GitHub的Web服務接口 https://developer.github.com/v3/ 包含了更多的特性。
|
||||
|
||||
**練習 4.10:** 脩改issues程序,根據問題的時間進行分類,比如不到一個月的、不到一年的、超過一年。
|
||||
|
||||
**練習 4.11:** 編寫一個工具,允許用戶在命令行創建、讀取、更新和刪除GitHub上的issue,當必要的時候自動打開用戶默認的編輯器用於輸入文本信息。
|
||||
|
||||
**練習 4.12:** 流行的web漫畵服務xkcd也提供了JSON接口。例如,一個 https://xkcd.com/571/info.0.json 請求將返迴一個很多人喜愛的571編號的詳細描述。下載每個鏈接(隻下載一次)然後創建一個離線索引。編寫一個xkcd工具,使用這些離線索引,打印和命令行輸入的檢索詞相匹配的漫畵的URL。
|
||||
|
||||
**練習 4.13:** 使用開放電影數據庫的JSON服務接口,允許你檢索和下載 https://omdbapi.com/ 上電影的名字和對應的海報圖像。編寫一個poster工具,通過命令行輸入的電影名字,下載對應的海報。
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user