Json是一種輕量級(jí)數(shù)據(jù)交換格式榨婆,具有靈活窿锉、易于閱讀的特點(diǎn),在互聯(lián)網(wǎng)行業(yè)有廣泛的應(yīng)用轧简。Go語(yǔ)言運(yùn)行時(shí)里自帶了encoding/json包驰坊,提供了Marshal()和Unmarshal()兩個(gè)函數(shù)進(jìn)行編碼和解碼,兩個(gè)函數(shù)原型如下:
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))
}
用法很簡(jiǎn)單拳芙,不過有兩個(gè)問題需要說明一下:
結(jié)構(gòu)體Foo里的字段必須是大寫字母開頭,否則將不能從Json中解析出對(duì)應(yīng)的字段皮璧。原因是Go語(yǔ)言約定一個(gè)包中只有首字母大寫的符號(hào)才被導(dǎo)出給其他包使用舟扎。json.Unmarshal()函數(shù)是在另外一個(gè)包里的,所以如果字段名小寫悴务,就無(wú)法進(jìn)行賦值睹限。
-
這種寫法默認(rèn)把Json里的字段賦值給結(jié)構(gòu)體Foo里的同名字段,但是兩者的字段名并不總是能保持一致讯檐,比如Json里的字段名可能是小寫的name和age羡疗。這種情況下就需要手動(dòng)指定字段的對(duì)應(yīng)關(guān)系:
type Foo struct { Name string `json:"name"` Age int `json:"value"` }
結(jié)構(gòu)體Foo里的字段不但可以是基本類型,也可以是其他的結(jié)構(gòu)體裂垦,比如下面這個(gè)例子:
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))
}
前面這兩個(gè)例子里顺囊,Json的結(jié)構(gòu)都是已經(jīng)確定了的肌索,因此才可以預(yù)先定義好結(jié)構(gòu)體蕉拢,然后用Marshal()和Unmarshal()在Go對(duì)象和Json串之間進(jìn)行轉(zhuǎn)換。有時(shí)候在解析之前我們可能并不知道Json對(duì)象里有哪些字段诚亚,這就需要一種更靈活的處理方式晕换。在Python里,json.loads()直接將Json串轉(zhuǎn)換成字典站宗。類似的闸准,在Go語(yǔ)言里,可以用把字段不確定的Json串轉(zhuǎn)換成map梢灭∫募遥看下面這個(gè)例子:
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對(duì)應(yīng)了Json對(duì)象里結(jié)構(gòu)不確定的extra字段。Go語(yǔ)言的map在聲明時(shí)必須指定key和value的類型(這點(diǎn)遠(yuǎn)不如Python靈活)敏释,但是extra里的字段可能是不同類型库快,因此這個(gè)例子里使用了interface作為value類型。interface也就是“接口”钥顽,Go語(yǔ)言里任何數(shù)據(jù)類型都是某種接口义屏,因此interface類型可以指代任何類型。當(dāng)然我們也可以自定義interface類型,這不在本文的討論范圍之內(nèi)闽铐。按照我的理解蝶怔,interface類型在這里的作用類似于C語(yǔ)言里的void *。
還有一點(diǎn)值得一提的是兄墅,例子里的childrens字段值雖然是整數(shù)踢星,但是被解析成了float64類型。在Json的定義中察迟,值的類型不分整型和浮點(diǎn)型斩狱,只有一個(gè)number類型,因此在我們沒有指定字段和類型的情況下扎瓶,Unmarshal()函數(shù)把所有number類型的值都當(dāng)作float64所踊。我也是在寫這篇文章的時(shí)候才發(fā)現(xiàn)這一點(diǎn)的。
有了這些技巧概荷,基本上已經(jīng)能應(yīng)付絕大部分的Json處理任務(wù)了秕岛。如果還是覺得不夠好用,可以嘗試一下bit.ly的simplejson误证,或者性能更好的easyjson继薛。