本文是基于 Go 官方和 https://eager.io/blog/go-and-json/ 進(jìn)行翻譯整理的
JSON 是一種輕量級(jí)的數(shù)據(jù)交換格式萍膛,常用作前后端數(shù)據(jù)交換,Go 在 encoding/json 包中提供了對(duì) JSON 的支持嚷堡。
序列化
把 Go struct 序列化成 JSON 對(duì)象蝗罗,Go 提供了 Marshal 方法艇棕,正如其含義所示表示編排序列化,函數(shù)簽名如下:
func Marshal(v interface{}) ([]byte, error)
舉例來(lái)說(shuō)串塑,比如下面的 Go struct:
type Message struct {
Name string
Body string
Time int64
}
使用 Marshal 序列化:
m := Message{"Alice", "Hello", 1294706395881547000}
b, err := json.Marshal(m)
fmt.Println(b) //{"Name":"Alice","Body":"Hello","Time":1294706395881547000}
在 Go 中并不是所有的類型都能進(jìn)行序列化:
- JSON object key 只支持 string
- Channel沼琉、complex、function 等 type 無(wú)法進(jìn)行序列化
- 數(shù)據(jù)中如果存在循環(huán)引用桩匪,則不能進(jìn)行序列化打瘪,因?yàn)樾蛄谢瘯r(shí)會(huì)進(jìn)行遞歸
- Pointer 序列化之后是其指向的值或者是 nil
還需要注意的是:只有 struct 中支持導(dǎo)出的 field 才能被 JSON package 序列化,即首字母大寫的 field傻昙。
反序列化
反序列化函數(shù)是 Unmarshal 闺骚,其函數(shù)簽名如下:
func Unmarshal(data []byte, v interface{}) error
如果要進(jìn)行反序列化,我們首先需要?jiǎng)?chuàng)建一個(gè)可以接受序列化數(shù)據(jù)的 Go struct:
var m Message
err := json.Unmarshal(b, &m)
JSON 對(duì)象一般都是小寫表示妆档,Marshal 之后 JSON 對(duì)象的首字母依然是大寫僻爽,如果序列化之后名稱想要改變?nèi)绾螌?shí)現(xiàn),答案就是 struct tags贾惦。
Struct Tag
Struct tag 可以決定 Marshal 和 Unmarshal 函數(shù)如何序列化和反序列化數(shù)據(jù)胸梆。
指定 JSON filed name
JSON object 中的 name 一般都是小寫,我們可以通過(guò) struct tag 來(lái)實(shí)現(xiàn):
type MyStruct struct {
SomeField string `json:"some_field"`
}
SomeField 序列化之后會(huì)變成 some_field须板。
指定 field 是 empty 時(shí)的行為
使用 omitempty
可以告訴 Marshal 函數(shù)如果 field 的值是對(duì)應(yīng)類型的 zero-value碰镜,那么序列化之后的 JSON object 中不包含此 field:
type MyStruct struct {
SomeField string `json:"some_field,omitempty"`
}
m := MyStruct{}
b, err := json.Marshal(m) //{}
如果 SomeField == “”
,序列化之后的對(duì)象就是 {}
逼纸。
跳過(guò) field
Struct tag “-” 表示跳過(guò)指定的 filed:
type MyStruct struct {
SomeField string `json:"some_field"`
Passwd string `json:"-"`
}
m := MyStruct{}
b, err := json.Marshal(m) //{"some_feild":""}
即序列化的時(shí)候不輸出洋措,這樣可以有效保護(hù)需要保護(hù)的字段不被序列化。
反序列化任意 JSON 數(shù)據(jù)
默認(rèn)的 JSON 只支持以下幾種 Go 類型:
bool
for JSON booleansfloat64
for JSON numbersstring
for JSON stringsnil
for JSON null
在序列化之前如果不知道 JSON 數(shù)據(jù)格式杰刽,我們使用 interface{}
來(lái)存儲(chǔ)菠发。interface {}
的作用詳見本博的其他文章。
有如下的數(shù)據(jù)格式:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
如果我們序列化之前不知道其數(shù)據(jù)格式贺嫂,我們可以使用 interface{}
來(lái)存儲(chǔ)我們的 decode 之后的數(shù)據(jù):
var f interface{}
err := json.Unmarshal(b, &f)
反序列化之后 f 應(yīng)該是像下面這樣:
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}
key 是 string滓鸠,value 是存儲(chǔ)在 interface{} 內(nèi)的。想要獲得 f 中的數(shù)據(jù)第喳,我們首先需要進(jìn)行 type assertion糜俗,然后通過(guò) range 迭代獲得 f 中所有的 key :
m := f.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case float64:
fmt.Println(k, "is float64", 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")
}
}
反序列化對(duì) slice、map曲饱、pointer 的處理
我們定義一個(gè) struct 繼續(xù)對(duì)上面例子中的 b
進(jìn)行反序列化:
type FamilyMember struct {
Name string
Age int
Parents []string
}
var m FamilyMember
err := json.Unmarshal(b, &m)
這個(gè)例子是能夠正常工作的悠抹,你一定也注意到了,struct 中包含一個(gè) slice Parents
扩淀,slice 默認(rèn)是 nil楔敌,之所以反序列化可以正常進(jìn)行就是因?yàn)?Unmarshal 在序列化時(shí)進(jìn)行了對(duì) slice Parents
做了初始化,同理驻谆,對(duì) map 和 pointer 都會(huì)做類似的工作卵凑,比如序列化如果 Pointer 不是 nil 首先進(jìn)行 dereference 獲得其指向的值庆聘,然后再進(jìn)行序列化,反序列化時(shí)首先對(duì) nil pointer 進(jìn)行初始化
Stream JSON
除了 marshal 和 unmarshal 函數(shù)勺卢,Go 還提供了 Decoder 和 Encoder 對(duì) stream JSON 進(jìn)行處理伙判,常見 request 中的 Body、文件等:
jsonFile, err := os.Open("post.json")
if err != nil {
fmt.Println("Error opening json file:", err)
return
}
defer jsonFile.Close()
decoder := json.NewDecoder(jsonFile)
for {
var post Post
err := decoder.Decode(&post)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("error decoding json:", err)
return
}
fmt.Println(post)
}
嵌入式 struct 的序列化
Go 支持對(duì) nested struct 進(jìn)行序列化和反序列化:
type App struct {
Id string `json:"id"`
}
type Org struct {
Name string `json:"name"`
}
type AppWithOrg struct {
App
Org
}
func main() {
data := []byte(`
{
"id": "k34rAT4",
"name": "My Awesome Org"
}
`)
var b AppWithOrg
json.Unmarshal(data, &b)
fmt.Printf("%#v", b)
a := AppWithOrg{
App: App{
Id: "k34rAT4",
},
Org: Org{
Name: "My Awesome Org",
},
}
data, _ = json.Marshal(a)
fmt.Println(string(data))
}
Nested struct 雖然看起來(lái)有點(diǎn)怪異黑忱,有些時(shí)候它將非常有用宴抚。
自定義序列化函數(shù)
Go JSON package 中定了兩個(gè) Interface Marshaler 和 Unmarshaler ,實(shí)現(xiàn)這兩個(gè) Interface 可以讓你定義的 type 支持序列化操作杨何。
錯(cuò)誤處理
總是記得檢查序列或反序列化的錯(cuò)誤酱塔,可以讓你的程序更健壯,而不是在出錯(cuò)之后帶著錯(cuò)誤繼續(xù)執(zhí)行下去危虱。