這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。 JSON(Javascript Object Notation)是一種輕量級的資料交換語言,以文字為基礎,具有自我描述性且易於讓人閱讀。儘管JSON是JavaScript的一個子集,但JSON是獨立於語言的文字格式設定,並且採用了類似於C語言家族的一些習慣。JSON與XML最大的不同在於XML是一個完整的標記語言,而JSON不是。JSON由於比XML更小、更快,更易解析,以及瀏覽器的內建快速解析支援,使得其更適用於網路資料轉送領域。
解析到結構體Go的JSON包中有如下函數
// json.gopackage mainimport ("encoding/json""fmt")type Server struct {ServerName stringServerIP string}type Serverslice struct {Servers []Server}func main() {var s Serverslicestr := `{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}`json.Unmarshal([]byte(str), &s)fmt.Println(s)fmt.Println(s.Servers[0].ServerIP)}outputs:{[{Shanghai_VPN 127.0.0.1} {Beijing_VPN 127.0.0.2}]}127.0.0.1 首先定義了與json資料對應的結構體,數組對應slice,欄位名對應JSON裡面的KEY,在解析的時候,如何將JSON資料與struct欄位相匹配呢?如JSON的key是Foo,那麼怎麼找對應的欄位呢?
- 首先尋找tag含有Foo的可匯出的struct欄位(首字母大寫)
- 最後尋找類似FOO或者FoO這樣的除了首字母之外其他大小寫不敏感的匯出欄位
能夠被賦值的欄位必須是可匯出欄位(即首字母大寫)。同時JSON解析的時候只會解析能找到的欄位,如果找不到的欄位會被忽略,這樣的一個好處是:當你接收到一個很大的JSON資料結構而你卻只想擷取其中的部分資料的時候,你只需將你想要的資料對應的欄位名大寫,即可輕鬆解決問題。
解析到interface 我們知道interface{}可以用來儲存任意資料類型的對象,這種資料結構正好用於儲存解析的未知結構的json資料的結果。JSON包中採用map[string]interface{}和[]interface{}結構來儲存任意的JSON對象和數組。Go類型和JSON類型的對應關係如下:
通過斷言方式訪問資料,如下例:
// tointer.gopackage mainimport ("encoding/json""fmt")func main() {b := []byte(`{"Name":"Wednesday", "Age":6, "Parents": [ "Gomez", "Moticia" ]}`)var f interface{}err := json.Unmarshal(b, &f)if err != nil {fmt.Println(err)}m := f.(map[string]interface{})for k, v := range m {switch vv := v.(type) {case string:fmt.Println(k, "is string", vv)case int:fmt.Println(k, "is int", vv)case []interface{}:fmt.Println(k, "is an array:")for i, u := range vv {fmt.Println(i, u)}default:fmt.Println(k, "is of a type I don't know how to handle")}}}outputs: Name is string Wednesday Age is of a type I don't know how to handle Parents is an array: 0 Gomez 1 Moticia通過上面的樣本可以看到,通過interface{}與type assert的配合,我們就可以解析未知結構的JSON函數了。產生JSON若我們要輸出JSON資料串,可通過Marshal函數來處理
func Marshal(v interface{})([]byte, error)例:
// generalJsonpackage mainimport ("encoding/json""fmt")type Server struct {ServerName string `json:"serverName"`ServerIP string `json:"serverIP"`}type Serverslice struct {Servers []Server `json:"servers"`}func main() {var s Serverslices.Servers = append(s.Servers, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})s.Servers = append(s.Servers, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})b, err := json.Marshal(s)if err != nil {fmt.Println("json err: ", err)}fmt.Println(string(b))}outputs:{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"}, {"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]} 針對JSON的輸出,我們在定義struct tag的時候需要注意幾點:
- 欄位的tag是“-”,那麼這個欄位不會輸出到JSON
- tag中帶有自訂名稱,那麼這個自訂名稱會出現在JSON的欄位名中,例如上面例子中的serverName
- tag中如果帶有“omitempty”選項,那麼如果該欄位值為空白,就不會輸出到JSON串中
- 如果欄位類型是bool,string,int,int64等,而tag中帶有“,string”選項,那麼這個欄位在輸出到JSON的時候會把該欄位對應的值轉換成JSON字串
例:
// jsontest.gopackage mainimport ("encoding/json""os")type Server struct {//ID不會匯出到JSONID int `json:"-"`//ServerName的值會進行二次JSON編碼ServerName string `json:"serverName"`ServerName2 string `json:"serverName2, string"`//如果ServerIP為空白,則不輸出到JSON中ServerIP string `json:"serverIP,omitempty"`Description string `json:"description,string"`}func main() {s := Server{ID: 3,ServerName: `Go "1.0"`,ServerName2: `Go "1.0"`,ServerIP: ``,Description: `描述資訊`,}b, _ := json.Marshal(s)os.Stdout.Write(b)}outputs:{"serverName":"Go \"1.0\"","serverName2":"Go \"1.0\""} Marshal函數只有在轉換成功的時候才會返回資料,在轉換的過程中我們需要注意幾點:
- JSON對象只支援string作為key,所以要編碼一個map,那麼必須是map[string]T這種類型(T是Go語言中任意的類型)
- Channel,complex和function是不能被編碼成JSON的
- 嵌套的資料時不能編碼的,不然會讓JSON編碼進入死迴圈
- 指標在編碼的時候會輸出指標指向的內容,而null 指標會輸出null
目前bitly公司開源了一個叫做simplejson的包,在處理未知結構體的JSON時相當方便,地址:https://github.com/bitly/go-simplejson