Json是一種輕量級數據交換格式,具有靈活、易于閱讀的特點,在互聯網行業有廣泛的應用。Go語言運行時里自帶了encoding/json包,提供了Marshal()和Unmarshal()兩個函數進行編碼和解碼,兩個函數原型如下:
func Marshal(v interface{}) ([]byte, error)
func Unmarshal(data []byte, v interface{}) error
廢話少說,直接上例子:
package main
import (
"fmt"
"encoding/json"
)
type Foo struct {
Name string
Age int
}
func main() {
data := []byte(`{"Name": "John Doe", "Age": 25}`)
var f Foo
json.Unmarshal(data, &f)
fmt.Printf("%s is %d years old.\n", f.Name, f.Age)
output, _ := json.Marshal(&f)
fmt.Println(string(output))
}
用法很簡單,不過有兩個問題需要說明一下:
結構體Foo里的字段必須是大寫字母開頭,否則將不能從Json中解析出對應的字段。原因是Go語言約定一個包中只有首字母大寫的符號才被導出給其他包使用。json.Unmarshal()函數是在另外一個包里的,所以如果字段名小寫,就無法進行賦值。
-
這種寫法默認把Json里的字段賦值給結構體Foo里的同名字段,但是兩者的字段名并不總是能保持一致,比如Json里的字段名可能是小寫的name和age。這種情況下就需要手動指定字段的對應關系:
type Foo struct { Name string `json:"name"` Age int `json:"value"` }
結構體Foo里的字段不但可以是基本類型,也可以是其他的結構體,比如下面這個例子:
type Employment struct {
Company string `json:"company"`
Title string `json:"title"`
}
type Foo struct {
Name string `json:"name"`
Age int `json:"age"`
Job Employment `json:"job"`
}
func main() {
data := []byte(`{"name": "John Doe", "age": 25, "job": {"company": "ABC", "title": "Engineer"}}`)
var f Foo
json.Unmarshal(data, &f)
fmt.Printf("%s is %d years old.\n", f.Name, f.Age)
output, _ := json.Marshal(&f)
fmt.Println(string(output))
}
前面這兩個例子里,Json的結構都是已經確定了的,因此才可以預先定義好結構體,然后用Marshal()和Unmarshal()在Go對象和Json串之間進行轉換。有時候在解析之前我們可能并不知道Json對象里有哪些字段,這就需要一種更靈活的處理方式。在Python里,json.loads()直接將Json串轉換成字典。類似的,在Go語言里,可以用把字段不確定的Json串轉換成map。看下面這個例子:
type Foo struct {
Name string `json:"name"`
Age int `json:"age"`
Job Employment `json:"job"`
Extra map[string]interface{} `json:"extra"`
}
func main() {
data := []byte(`{
"name": "John Doe",
"age": 25,
"job": {
"company": "ABC",
"title": "Engineer"
},
"extra": {
"marital status": "married",
"childrens": 0
}}`)
var f Foo
json.Unmarshal(data, &f)
fmt.Printf("%s is %d years old and works at %s.\n", f.Name, f.Age, f.Job.Company)
fmt.Printf("He is %s and has %d childrens.\n", f.Extra["marital status"].(string), int(f.Extra["childrens"].(float64)))
output, _ := json.Marshal(&f)
fmt.Println(string(output))
}
Extra對應了Json對象里結構不確定的extra字段。Go語言的map在聲明時必須指定key和value的類型(這點遠不如Python靈活),但是extra里的字段可能是不同類型,因此這個例子里使用了interface作為value類型。interface也就是“接口”,Go語言里任何數據類型都是某種接口,因此interface類型可以指代任何類型。當然我們也可以自定義interface類型,這不在本文的討論范圍之內。按照我的理解,interface類型在這里的作用類似于C語言里的void *。
還有一點值得一提的是,例子里的childrens字段值雖然是整數,但是被解析成了float64類型。在Json的定義中,值的類型不分整型和浮點型,只有一個number類型,因此在我們沒有指定字段和類型的情況下,Unmarshal()函數把所有number類型的值都當作float64。我也是在寫這篇文章的時候才發現這一點的。
有了這些技巧,基本上已經能應付絕大部分的Json處理任務了。如果還是覺得不夠好用,可以嘗試一下bit.ly的simplejson,或者性能更好的easyjson。