Json(Javascript Object Nanotation)是一種數(shù)據(jù)交換格式,常用于前后端數(shù)據(jù)傳輸。任意一端將數(shù)據(jù)轉(zhuǎn)換成json字符串,另一端再將該字符串解析成相應(yīng)的數(shù)據(jù)結(jié)構(gòu),如string類型,strcut對象等。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,它會導(dǎo)致marshal陷入死循環(huán)。 -
channel,complex以及函數(shù)不能被編碼json字符串。
- 指針變量編碼時自動轉(zhuǎn)換為它所指向的值
指針變量編碼時自動轉(zhuǎn)換為它所指向的值,與直接定義為結(jié)構(gòu)體對象類型效果一樣,只不過指針更快,且能節(jié)省內(nèi)存空間。 - 對象序列化為json后就成為純粹的字符串。
- 包含通用類型的對象序列化
成員變量都是已知的類型,只能接收指定的類型,比如string類型的Name只能賦值string類型的數(shù)據(jù)。
有時為了通用性或使代碼簡潔,我們希望有一種類型可以接受各種類型的數(shù)據(jù),并序列化為json,就需要使用interface{}類型。無論是string,int,bool,還是指針類型等,都可賦值給interface{}類型,且正常編碼,效果與前面的例子一樣。
備注:interface{}類型其實是個空接口,即沒有方法的接口。go的每一種類型都實現(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ù)據(jù),且Unmarshal時接收體必須傳遞指針。否則解析雖不報錯,但數(shù)據(jù)無法賦值到接收體中。
- json字符串key和對象字段的匹配規(guī)則
解析時,接收體可自行定義。json串中的key自動在接收體中尋找匹配的字段進行賦值。匹配規(guī)則是:
- 先查找與key一樣的json標(biāo)簽,找到則賦值給該標(biāo)簽對應(yīng)的變量(如Name)。
- 沒有json標(biāo)簽的,就從上往下依次查找變量名與key一樣的變量,如Age?;蛘咦兞棵雎源笮懞笈ckey一樣的變量,如HIgh,Class。第一個匹配的就賦值,后面就算有匹配的也忽略。
- 可解析的變量必須是可導(dǎo)出的,即首字母大寫。不可導(dǎo)出的變量無法被解析(即使json串中有對應(yīng)key的k-v,解析后其值仍為nil,即空值)。
- 當(dāng)接收體中存在json串中匹配不了的項時,解析會自動忽略該項,該項仍保留原值。如沒有初始值,保留空值nil。
- interface{}類型的變量,如果解析時沒有明確指定字段的類型,可能得不到自己期望的數(shù)據(jù)結(jié)構(gòu)。例如:解析時不指定變量的具體類型(定義為interface{}類型),json自動將value為復(fù)合結(jié)構(gòu)的數(shù)據(jù)解析為map[string]interface{}類型的項。
- interface{}類型變量的類型(
reflect.TypeOf(value))都為nil,就是沒有具體類型,這是空接口(interface{}類型)的特點。 -
簡單數(shù)據(jù)如基本數(shù)據(jù)類型的數(shù)據(jù)只進行一次json解析。
復(fù)合數(shù)據(jù)如切片、數(shù)據(jù)結(jié)構(gòu)等數(shù)據(jù),可進行二次甚至多次json解析的,因為它的value也是個可被解析的獨立json。對于”復(fù)合數(shù)據(jù)”,如果接收體中的項被聲明為interface{}類型,go都會默認(rèn)解析成map[string]interface{}類型。如果我們想直接解析到期望的數(shù)據(jù)結(jié)構(gòu)對象中,可以將接收體對應(yīng)的項定義為具體的struct類型。 - 保留反序列化中的
interface{}類型
如果不想指定變量為具體的類型,仍想保留interface{}類型,但又希望該變量可以解析到對象中,可以將該變量定義為json.RawMessage類型。如此做之后在接收體中,被聲明為json.RawMessage類型的變量在json解析時,變量值仍保留json的原值,即未被自動解析為map[string]interface{}類型。我們可以對該變量進行二次json解析,因為其值仍是個獨立且可解析的完整json串。我們只需再定義一個新的接收體(具體的結(jié)構(gòu)體)即可。