gopl-zh.github.com/ch4/ch4-05.md

5.0 KiB
Raw Blame History

4.5. 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包的用法做個概述。

JSON是對JavaScript中各種值——字符串、數字、布爾值和對象——Unicode本文編碼。它可以用有效可讀的方式表示第三章的基礎數據類型和本章的數組、slice、結構體和map等聚合數據類型。

基本的JSON類型有數字十進製或科學記數法、布爾值true或false、字符串其中字符串是以雙引號包含的Unicode字符序列支持和Go語言類似的反斜槓轉義特性不過JSON使用的是\Uhhhh轉義數字來表示一個UTF-16編碼而不是Go語言的rune類型。

這些基礎類型可以通過JSON的數組和對象類型進行遞歸組合。一個JSON數組是一個有序的值序列寫在一個方括號中併以逗號分隔一個JSON數組可以用於編碼Go語言的數組和slice。一個JSON對象是一個字符串到值的映射寫成以繫列的name:value對形式用花括號包含併以逗號分隔JSON的對象類型可以用於編碼Go語言的map類型key類型是字符串和結構體。例如

boolean         true
number          -273.15
string          "She said \"Hello, BF\""
array           ["gold", "silver", "bronze"]
object          {"year": 1980,
                 "event": "archery",
                 "medals": ["gold", "silver", "bronze"]}

考慮一個應用程序該程序負責收集各種電影評論併提供反饋功能。它的Movie數據類型和一個典型的表示電影的值列表如下所示。其中結構體聲明中Year和Color成員後面的字符串面值是結構體成員Tag我們稍後會解釋它的作用。

gopl.io/ch4/movie

type Movie struct {
	Title  string
	Year   int  `json:"released"`
	Color  bool `json:"color,omitempty"`
	Actors []string
}

var movies = []Movie{
	{Title: "Casablanca", Year: 1942, Color: false,
		Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
	{Title: "Cool Hand Luke", Year: 1967, Color: true,
		Actors: []string{"Paul Newman"}},
	{Title: "Bullitt", Year: 1968, Color: true,
		Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
	// ...
}

這樣的數據結構特别適合JSON格式併且在兩種之間相互轉換也很容易。將一個Go語言中類似movies的結構體slice轉爲JSON的過程叫編組marshaling。編組通過調用json.Marshal函數完成

data, err := json.Marshal(movies)
if err != nil {
	log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)

Marshal函數生成一個字節slice包含很長的字符串併且沒有空白縮進我們將它摺行以便於顯示

[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
id Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Ac
tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
Actors":["Steve McQueen","Jacqueline Bisset"]}]

這種緊湊的表示形式雖然包含了全部的信息但是很難閲讀。爲了生成便於閲讀的格式另一個json.MarshalIndent函數將産生整齊縮進的輸出。有兩個額外的字符串參數用於表示每一行輸出的前綴和每一個層級的縮進

data, err := json.MarshalIndent(movies, "", "    ")
if err != nil {
	log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)

上面的代碼將産生這樣的輸出:

[
	{
		"Title": "Casablanca",
		"released": 1942,
		"Actors": [
			"Humphrey Bogart",
			"Ingrid Bergman"
		]
	},
	{
		"Title": "Cool Hand Luke",
		"released": 1967,
		"color": true,
		"Actors": [
			"Paul Newman"
		]
	},
	{
		"Title": "Bullitt",
		"released": 1968,
		"color": true,
		"Actors": [
			"Steve McQueen",
			"Jacqueline Bisset"
		]
	}
]

在編碼時默認使用Go語言結構體的成員名字作爲JSON的對象通過reflect反射技術我們將在12.6節討論)。隻有導出的結構體成員才會被編碼,這也就是我們爲什麽選擇用大寫字母開頭的成員名稱。

細心的讀者可能已經註意到其中Year名字的成員在編碼後變成了released還有Color長遠編碼後變成了小寫字母開頭的color。這是因爲構體成員Tag所導致的。一個構體成員Tag是和在編譯階段關聯到該成員的元信息字符串

Year  int  `json:"released"`
Color bool `json:"color,omitempty"`

TODO