diff --git a/ch4/ch4-05.md b/ch4/ch4-05.md index 276ad1d..907e571 100644 --- a/ch4/ch4-05.md +++ b/ch4/ch4-05.md @@ -1,3 +1,116 @@ ## 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;我們稍後會解釋它的作用。) + +```Go +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函數完成: + +```Go +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函數將産生整齊縮進的輸出。有兩個額外的字符串參數用於表示每一行輸出的前綴和每一個層級的縮進: + +```Go +data, err := json.MarshalIndent(movies, "", " ") +if err != nil { + log.Fatalf("JSON marshaling failed: %s", err) +} +fmt.Printf("%s\n", data) +``` + +上面的代碼將産生這樣的輸出: + +```Json +[ + { + "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