go語言本身為我們提供了json的工具包”encoding/json”。
前言:
Json–Javascript Object Nanotation 是一種數據交換格式,經常用於前后端的數據傳輸。一端將數據轉換成json字符串,另一端再將json字符串轉換成相應的數據結構,如struct, float等。
用法:
1.Marshal—將數據編碼成json字符串
package main
import (
"encoding/json"
"fmt"
)
type Student struct{
Name string `json:"name"`
Age int
sex string
Class *Class `json:"class"`
}
type Class struct {
Name string
Grade int
}
func main() {
//實例化一個數據結構,用於生成json字符串
stu := Student{
Name: "Raily",
Age: 18,
sex: "女",
}
//指針變量
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu.Class = cla
jsonStu, err := json.Marshal(stu)
if err!= nil{
return
}
//jsonStu是[]byte類型,轉化成string類型便於查看
fmt.Println(string(jsonStu))
}
輸出結果:
{"name":"Raily","Age":18,"class":{"Name":"1班","Grade":3}}
- 1
從結果中可以看出:
①只要是可導出成員(變量首字母大寫),都可以轉成json。因成員變量sex是不可導出的,故無法轉成json。
②如果變量打上了json標簽,如Name旁邊的 json:"name"
,那么轉化成的json key就用該標簽“name”,否則取變量名作為key,如“Age”,“HIgh”。
③bool類型也是可以直接轉換為json的value值。Channel, complex 以及函數不能被編碼json字符串。當然,循環的數據結構也不行,它會導致marshal陷入死循環。
④指針變量,編碼時自動轉換為它所指向的值,如cla變量。(當然,不傳指針,Stu struct的成員Class如果換成Class struct類型,效果也是一模一樣的。只不過指針更快,且能節省內存空間。)
最后,強調一句:json編碼成字符串后就是純粹的字符串了。
············································································································
上面的成員變量都是已知的類型,只能接收指定的類型,比如string類型的Name只能賦值string類型的數據。
但有時為了通用性,或使代碼簡潔,我們希望有一種類型可以接受各種類型的數據,並進行json編碼。這就用到了interface{}類型。
如下:
type Student struct {
Name interface{} `json:"name"`
Age interface{}
sex interface{}
Class interface{} `json:"class"`
}
//其他部分與上面的例子一樣;無論是string,int,bool,還是指針類型等,都可賦值給interface{}類型。
············································································································
在實際項目中,編碼成json串的數據結構,往往是切片類型。如下定義了一個[]StuRead類型的切片:
//方式1:只聲明,不分配內存
var stus1 []*StuRead
//方式2:分配初始值為0的內存
stus2 := make([]*StuRead, 0)
//錯誤示范
//new()只能實例化一個struct
stus := new([]StuRead)
stu1 := StuRead{成員賦值...}
stu2 := StuRead{成員賦值...}
//由方式1和2創建的切片,都能成功追加數據
//方式2最好分配0長度,append時會自動增長。反之指定初始長度,長度不夠時不會自動增長,導致數據丟失
stus1 := appen(stus1,stu1,stu2)
stus2 := appen(stus2,stu1,stu2)
//成功編碼
json1,_ := json.Marshal(stus1)
json2,_ := json.Marshal(stus2)
解碼時定義對應的切片接受即可。
2.Unmarshal—將json字符串解碼到相應的數據結構
將上面的例子進行解碼:
type StuRead struct {
Name interface{} `json:"name"`
Age interface{}
sex interface{}
Class interface{} `json:"class"`
Test interface{}
}
type Class struct {
Name string
Grade int
}
func main() {
//json字符中的"引號,需用\進行轉義,否則編譯出錯
//json字符串沿用上面的結果,但對key進行了大小的修改,並添加了sex數據
data:="{\"name\":\"張三\",\"Age\":18,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}"
str:=[]byte(data)
//1.Unmarshal的第一個參數是json字符串,第二個參數是接受json解析的數據結構。
//第二個參數必須是指針,否則無法接收解析的數據,如stu仍為空對象StuRead{}
//2.可以直接stu:=new(StuRead),此時的stu自身就是指針
stu:=StuRead{}
err:=json.Unmarshal(str,&stu)
//解析失敗會報錯,如json字符串格式不對,缺"號,缺}等。
if err!=nil{
fmt.Println(err)
}
fmt.Println(stu)
}
結果:
{張三 18 true <nil> map[naME:1班 GradE:3] <nil>}
總結:
① json字符串解析時,需要一個“接收體”接受解析后的數據,且Unmarshal時接收體必須傳遞指針。否則解析雖不報錯,但數據無法賦值到接受體中。如這里用的是StuRead{}接收。
② 解析時,接收體可自行定義。json串中的key自動在接收體中尋找匹配的項進行賦值。匹配規則是:
先查找與key一樣的json標簽,找到則賦值給該標簽對應的變量(如Name)。
沒有json標簽的,就從上往下依次查找變量名與key一樣的變量,如Age。或者變量名忽略大小寫后與key一樣的變量。如Class。第一個匹配的就賦值,后面就算有匹配的也忽略。
(前提是該變量必需是可導出的,即首字母大寫)。
不可導出的變量無法被解析(如sex變量,雖然json串中有key為sex的k-v,解析后其值仍為nil,即空值)
③ 當接收體中存在json串中匹配不了的項時,解析會自動忽略該項,該項仍保留原值。如變量Test,保留空值nil。
④ 你一定會發現,變量Class貌似沒有解析為我們期待樣子。因為此時的Class是個interface{}類型的變量,而json串中key為CLASS的value是個復合結構,不是可以直接解析的簡單類型數據(如“張三”,18,true等)。所以解析時,由於沒有指定變量Class的具體類型,json自動將value為復合結構的數據解析為map[string]interface{}類型的項。也就是說,此時的struct Class對象與StuRead中的Class變量沒有半毛錢關系,故與這次的json解析沒有半毛錢關系。