Json(Javascript Object Nanotation)是一種數(shù)據(jù)交換格式蹦疑,常用于前后端數(shù)據(jù)傳輸例隆。任意一端將數(shù)據(jù)轉(zhuǎn)換成json
字符串冕杠,另一端再將該字符串解析成相應(yīng)的數(shù)據(jù)結(jié)構(gòu)炬守,如string類型毁枯,strcut對(duì)象等边败。go語言本身為我們提供了json的工具包encoding/json
袱衷。
1 序列化為json字符串
1.1 Marshal
package main
import (
"encoding/json"
"fmt"
"os"
)
func main ( ) {
type ColorGroup struct {
ID int
Name string `json:"name"`
Colors [ ] string
note string
}
group := ColorGroup {
ID : 1 ,
Name : "Reds" ,
Colors : [ ] string { "Crimson" , "Red" , "Ruby" , "Maroon" } ,
}
b , err := json. Marshal ( group )
if err != nil {
fmt. Println ( "error:" , err )
}
os. Stdout . Write ( b )
}
結(jié)果輸出:
{"ID":1,"name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
1.2 序列化備注
- 只有首字母是大寫的成員才可以序列化為JSON
只有可導(dǎo)出成員(變量首字母大寫)才可以序列化為json。因成員變量note
是不可導(dǎo)出的笑窜,故無法轉(zhuǎn)成json致燥。 - 序列化為JSON的字段名稱可以指定
如果變量打上了json
標(biāo)簽,如Name旁邊的json:"name"
排截,那么轉(zhuǎn)化成的json key就用該標(biāo)簽name
而不是Name
嫌蚤。
否則取變量名作為key,如ID
断傲,Colors
脱吱。 - 可以序列化為JSON的類型和限制
- 基本數(shù)據(jù)類型和普通的結(jié)構(gòu)體都可以序列化,如
bool
類型也是可以直接轉(zhuǎn)換為json的value值艳悔。循環(huán)的數(shù)據(jù)結(jié)構(gòu)不能序列化為JSON急凰,它會(huì)導(dǎo)致marshal陷入死循環(huán)。 -
channel
猜年,complex
以及函數(shù)
不能被編碼json字符串抡锈。
- 指針變量編碼時(shí)自動(dòng)轉(zhuǎn)換為它所指向的值
指針變量編碼時(shí)自動(dòng)轉(zhuǎn)換為它所指向的值,與直接定義為結(jié)構(gòu)體對(duì)象類型效果一樣乔外,只不過指針更快床三,且能節(jié)省內(nèi)存空間。 - 對(duì)象序列化為json后就成為純粹的字符串杨幼。
- 包含通用類型的對(duì)象序列化
成員變量都是已知的類型撇簿,只能接收指定的類型,比如string類型的Name只能賦值string類型的數(shù)據(jù)差购。
有時(shí)為了通用性或使代碼簡(jiǎn)潔四瘫,我們希望有一種類型可以接受各種類型的數(shù)據(jù),并序列化為json欲逃,就需要使用interface{}
類型找蜜。無論是string
,int
稳析,bool
洗做,還是指針類型
等,都可賦值給interface{}
類型彰居,且正常編碼诚纸,效果與前面的例子一樣。
備注:interface{}類型其實(shí)是個(gè)空接口陈惰,即沒有方法的接口畦徘。go的每一種類型都實(shí)現(xiàn)了該接口。因此,任何其他類型的數(shù)據(jù)都可以賦值給interface{}類型旧烧。 - 序列化為JSON字符串支持切片類型
切片類型的數(shù)據(jù)結(jié)構(gòu)可以序列化為JSON字符串影钉。
2 反序列化
2.1 Unmarshal
package main
import (
"encoding/json"
"fmt"
)
func main ( ) {
var jsonBlob = [ ] byte ( ` [
{ "Name" : "Platypus" , "Order" : "Monotremata" } ,
{ "Name" : "Quoll" , "Order" : "Dasyuromorphia" }
] ` )
type Animal struct {
Name string
Order string
}
var animals [ ] Animal
err := json. Unmarshal ( jsonBlob , & animals )
if err != nil {
fmt. Println ( "error:" , err )
}
fmt. Printf ( "%+v" , animals )
}
結(jié)果輸出:
[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
2.2 RawMeaage
package main
import (
"encoding/json"
"fmt"
"log"
)
func main ( ) {
type Color struct {
Space string
Point json. RawMessage // delay parsing until we know the color space
}
type RGB struct {
R uint8
G uint8
B uint8
}
type YCbCr struct {
Y uint8
Cb int8
Cr int8
}
var j = [ ] byte ( ` [
{ "Space" : "YCbCr" , "Point" : { "Y" : 255 , "Cb" : 0 , "Cr" : -10 } } ,
{ "Space" : "RGB" , "Point" : { "R" : 98 , "G" : 218 , "B" : 255 } }
] ` )
var colors [ ] Color
err := json. Unmarshal ( j , & colors )
if err != nil {
[log ](http://www.opengroup.org/onlinepubs/009695399/functions/log.html). Fatalln ( "error:" , err )
}
for _ , c := range colors {
var dst interface { }
switch c. Space {
case "RGB" :
dst = new ( RGB )
case "YCbCr" :
dst = new ( YCbCr )
}
err := json. Unmarshal ( c. Point , dst )
if err != nil {
[log ](http://www.opengroup.org/onlinepubs/009695399/functions/log.html). Fatalln ( "error:" , err )
}
fmt. Println ( c. Space , dst )
}
}
輸出結(jié)果:
YCbCr &{255 0 -10}
RGB &{98 218 255}
2.3 Decoder
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
func main ( ) {
const jsonStream = `
{ "Name" : "Ed" , "Text" : "Knock knock." }
{ "Name" : "Sam" , "Text" : "Who's there?" }
{ "Name" : "Ed" , "Text" : "Go fmt." }
{ "Name" : "Sam" , "Text" : "Go fmt who?" }
{ "Name" : "Ed" , "Text" : "Go fmt yourself!" }
`
type Message struct {
Name , Text string
}
dec := json. NewDecoder ( strings. NewReader ( jsonStream ) )
for {
var m Message
if err := dec. Decode ( & m ) ; err == io. EOF {
break
} else if err != nil {
[log ](http://www.opengroup.org/onlinepubs/009695399/functions/log.html). Fatal ( err )
}
fmt. Printf ( "%s: %s \n " , m. Name , m. Text )
}
}
輸出結(jié)果:
Ed: Knock knock.
Sam: Who's there?
Ed: Go fmt.
Sam: Go fmt who?
Ed: Go fmt yourself!
2.4 反序列化備注
- json字符串解析時(shí),需要一個(gè)接收體接收解析后的數(shù)據(jù)掘剪,且Unmarshal時(shí)接收體必須傳遞指針平委。否則解析雖不報(bào)錯(cuò)痊乾,但數(shù)據(jù)無法賦值到接收體中施逾。
- json字符串key和對(duì)象字段的匹配規(guī)則
解析時(shí),接收體可自行定義旗闽。json串中的key自動(dòng)在接收體中尋找匹配的字段進(jìn)行賦值匾鸥。匹配規(guī)則是:
- 先查找與key一樣的json標(biāo)簽蜡塌,找到則賦值給該標(biāo)簽對(duì)應(yīng)的變量(如Name)。
- 沒有json標(biāo)簽的勿负,就從上往下依次查找變量名與key一樣的變量馏艾,如Age∨洌或者變量名忽略大小寫后與key一樣的變量琅摩,如HIgh,Class锭硼。第一個(gè)匹配的就賦值房资,后面就算有匹配的也忽略。
- 可解析的變量必須是可導(dǎo)出的檀头,即首字母大寫轰异。不可導(dǎo)出的變量無法被解析(即使json串中有對(duì)應(yīng)key的k-v,解析后其值仍為nil,即空值)暑始。
- 當(dāng)接收體中存在json串中匹配不了的項(xiàng)時(shí)搭独,解析會(huì)自動(dòng)忽略該項(xiàng),該項(xiàng)仍保留原值廊镜。如沒有初始值戳稽,保留空值nil。
- interface{}類型的變量期升,如果解析時(shí)沒有明確指定字段的類型,可能得不到自己期望的數(shù)據(jù)結(jié)構(gòu)互躬。例如:解析時(shí)不指定變量的具體類型(定義為interface{}類型)播赁,json自動(dòng)將value為復(fù)合結(jié)構(gòu)的數(shù)據(jù)解析為map[string]interface{}類型的項(xiàng)。
- interface{}類型變量的類型(
reflect.TypeOf(value)
)都為nil
吼渡,就是沒有具體類型容为,這是空接口(interface{}
類型)的特點(diǎn)。 -
簡(jiǎn)單數(shù)據(jù)如基本數(shù)據(jù)類型的數(shù)據(jù)只進(jìn)行一次json解析。
復(fù)合數(shù)據(jù)如切片坎背、數(shù)據(jù)結(jié)構(gòu)等數(shù)據(jù)替劈,可進(jìn)行二次甚至多次json解析的,因?yàn)樗膙alue也是個(gè)可被解析的獨(dú)立json得滤。對(duì)于”復(fù)合數(shù)據(jù)”陨献,如果接收體中的項(xiàng)被聲明為interface{}
類型,go都會(huì)默認(rèn)解析成map[string]interface{}
類型懂更。如果我們想直接解析到期望的數(shù)據(jù)結(jié)構(gòu)對(duì)象中眨业,可以將接收體對(duì)應(yīng)的項(xiàng)定義為具體的struct類型。 - 保留反序列化中的
interface{}
類型
如果不想指定變量為具體的類型沮协,仍想保留interface{}
類型龄捡,但又希望該變量可以解析到對(duì)象中,可以將該變量定義為json.RawMessage
類型慷暂。如此做之后在接收體中聘殖,被聲明為json.RawMessage
類型的變量在json解析時(shí),變量值仍保留json的原值行瑞,即未被自動(dòng)解析為map[string]interface{}
類型奸腺。我們可以對(duì)該變量進(jìn)行二次json解析,因?yàn)槠渲等允莻€(gè)獨(dú)立且可解析的完整json串蘑辑。我們只需再定義一個(gè)新的接收體(具體的結(jié)構(gòu)體)即可洋机。