介紹
在本文中,我將向您展示如何在Go中解析和返回JSON的基礎(chǔ)知識疹启,同時(shí)還向您提供對該過程的理解蔼卡。
首先雇逞,讓我們看一下如何在其他語言中解析JSON塘砸,特別是動態(tài)類型的,我將以python為例廊宪。
##############
RETURNING JSON
##############
import json
# a Python object (dict):
p_dict = {
"name": "John",
"age": 30,
"city": "New York"
}
# convert into JSON:
y = json.dumps(p_dict)
# the result is a JSON string:
print(y)
##############
CONSUMING JSON
##############
import json
# some JSON:
j_str = '{ "name":"John", "age":30, "city":"New York"}'
# parse x:
y = json.loads(j_str)
# the result is a Python dictionary:
print(y["age"])
代碼看起來相對簡單箭启,在返回JSON的情況下册烈,我們有一個(gè)python字典“p_dict”赏僧,我們可以使用它轉(zhuǎn)換為JSON字符串json.dumps(p_dict)
扭倾。當(dāng)使用JSON時(shí)膛壹,我們有一個(gè)JSON字符串“j_str”,我們可以使用它轉(zhuǎn)換為python diction json.loads(j_str)
肩民。
現(xiàn)在持痰,在像Go這樣的靜態(tài)類型語言中解析像JSON這樣的格式會帶來一些問題工窍。Go是靜態(tài)類型的,這意味著程序中每個(gè)變量的類型都需要在編譯時(shí)知道鹏溯。這意味著您作為程序員必須指定每個(gè)變量的類型丙挽。其他語言(python)提供某種形式的類型推斷匀借,類型系統(tǒng)能夠推斷出變量的類型怀吻。靜態(tài)類型語言的主要優(yōu)點(diǎn)是所有類型的檢查都可以由編譯器完成初婆,因此在很早的階段就會發(fā)現(xiàn)許多瑣碎的錯(cuò)誤磅叛。一個(gè)簡單的代碼示例可能會澄清弊琴。
PYTHON
x = 3
GOLANG
var x int = 3
在解析JSON時(shí),任何東西都可以顯示在JSON主體中紫皇,那么編譯器如何知道如何設(shè)置內(nèi)存聪铺,因?yàn)樗恢李愋停?/p>
這有兩個(gè)答案铃剔。當(dāng)你知道你的數(shù)據(jù)會是什么樣子時(shí)會得到一個(gè)答案键兜,而當(dāng)你不了解數(shù)據(jù)時(shí)會得到答案穗泵。我們將首先介紹第一個(gè)選項(xiàng)佃延,因?yàn)檫@是最常見的情況并導(dǎo)致最佳實(shí)踐茎截。
當(dāng)JSON中的數(shù)據(jù)類型已知時(shí)企锌,您應(yīng)該將JSON解析為您定義的結(jié)構(gòu)撕攒。任何不適合結(jié)構(gòu)的字段都將被忽略烘浦。我們將在返回和使用JSON時(shí)探索此選項(xiàng)闷叉。
返回JSON
假設(shè)我們想要返回以下JSON對象握侧。
{
"key1": "value 1",
"key2": "value 2"
}
下面的代碼顯示了這是如何完成的,讓我們來談?wù)勊?/p>
package main
import (
"encoding/json"
"fmt"
)
type JSONResponse struct {
Value1 string `json:"key1"`
Value2 string `json:"key2"`
}
func main() {
jsonResponse := JSONResponse{
Value1: "Test value 1",
Value2: "Test value 2",
}
fmt.Printf("The struct returned before marshalling\n\n")
fmt.Printf("%+v\n\n\n\n", jsonResponse)
// The MarshalIndent function only serves to pretty print, json.Marshal() is what would normally be used
byteArray, err := json.MarshalIndent(jsonResponse, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Printf("The JSON response returned when the struct is marshalled\n\n")
fmt.Println(string(byteArray))
}
- 第3-6行:我們導(dǎo)入encoding / json和fmt包以解析JSON并打印結(jié)果。
- 第8-11行:JSONReponse結(jié)構(gòu)是JSON的Go變量表示萄传,與python字典是JSON的python變量表示的方式相同。
- 第8-11行:注意在JSONReponse中我們只聲明類型沒有值振诬,我們還添加“Go Tags”
json: "key1"
赶么,讓我們知道JSON對象中關(guān)聯(lián)的鍵是什么禽绪。 - 第15-18行:jsonResponse是JSONResponse結(jié)構(gòu)的實(shí)例化并填充值。
- 第20-21行:我們將jsonResponse變量打印到控制臺印屁,以便我們可以將其與稍后打印的JSON進(jìn)行對比雄人。
- 第23-24行:我們將變量jsonResponse編組為JSONResponse類型础钠,將其轉(zhuǎn)換為字節(jié)數(shù)組并考慮“Go Tags”中聲明的映射。返回的字節(jié)數(shù)組存儲在byteArray變量中踩萎。
- 第26-28行:我們對Marshal函數(shù)進(jìn)行錯(cuò)誤檢查香府,如果不是nil則打印錯(cuò)誤企孩。
- 第30-31行:我們將byteArray轉(zhuǎn)換為字符串并打印它袁稽,我們現(xiàn)在可以查看打印結(jié)構(gòu)和打印JSON之間的區(qū)別。
請?jiān)诖颂庍\(yùn)行代碼以查看結(jié)果补疑,使用代碼也是學(xué)習(xí)https://play.golang.org/p/gaBMvz21LiA的最佳方式癣丧。
嵌套的JSON
現(xiàn)在讓我們看一下具有嵌套項(xiàng)的更復(fù)雜的JSON對象
{
"key1": "value 1",
"key2": "value 2",
"nested": {
"nestkey1": "nest value 1",
"nestkey2": "nest value 2"
}
}
下面的代碼顯示了這是如何完成的栈妆,讓我們來討論改變了什么鳞尔。
package main
import (
"encoding/json"
"fmt"
)
type JSONResponse struct {
Value1 string `json:"key1"`
Value2 string `json:"key2"`
Nested Nested `json:"nested"`
}
type Nested struct {
NestValue1 string `json:"nestkey1"`
NestValue2 string `json:"nestkey2"`
}
func main() {
nested := Nested{
NestValue1: "nest value 1",
NestValue2: "nest value 2",
}
jsonResponse := JSONResponse{
Value1: "value 1",
Value2: "value 2",
Nested: nested,
}
// Try uncommenting the section below and commenting out lines 21-30 the result will be the same meaning you can declare inline
// jsonResponse := JSONResponse{
// Value1: "value 1",
// Value2: "value 2",
// Nested: Nested{
// NestValue1: "nest value 1",
// NestValue2: "nest value 2",
// },
// }
fmt.Printf("The struct returned before marshalling\n\n")
fmt.Printf("%+v\n\n\n\n", jsonResponse)
// The MarshalIndent function only serves to pretty print, json.Marshal() is what would normally be used
byteArray, err := json.MarshalIndent(jsonResponse, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Printf("The JSON response returned when the struct is marshalled\n\n")
fmt.Println(string(byteArray))
}
- 第11行:不是將Nested聲明為類型字符串,而是將Nested聲明為Nested類型霞扬,這是我們即將創(chuàng)建的類型。
- 第14-17行:我們聲明了我們在JSONResponse中使用的嵌套類型結(jié)構(gòu)萤彩,遵循我們想要返回的JSON的格式雀扶。
- 第21-30行:我們實(shí)例化Nested類型的嵌套變量和JSONResponse類型的jsonResponse變量肆汹,后者又引用我們剛剛聲明的嵌套變量。
這些是這個(gè)例子和前一個(gè)例子之間的主要區(qū)別扫腺。再次運(yùn)行此處的代碼進(jìn)行測試村象,并在評論https://play.golang.org/p/GcRceKe1jC-中進(jìn)行一些修改。
JSON中的數(shù)組
最后咧织,讓我們看一下返回包含數(shù)組的JSON對象习绢。
{
"key1": "value 1",
"key2": "value 2",
"nested": {
"nestkey1": "nest value 1",
"nestkey2": "nest value 2"
},
"arrayitems": [
{
"iteminfokey1": "item info 1 array index 0",
"iteminfokey2": "item info 2 array index 0"
},
{
"iteminfokey1": "item info 1 array index 1",
"iteminfokey2": "item info 2 array index 1"
}
]
}
下面的代碼顯示了這是如何完成的蝙昙,再次讓我們討論一下發(fā)生了什么變化奇颠。
package main
import (
"encoding/json"
"fmt"
)
type JSONResponse struct {
Value1 string `json:"key1"`
Value2 string `json:"key2"`
Nested Nested `json:"nested"`
ArrayItems []ArrayItem `json:"arrayitems"`
}
type Nested struct {
NestValue1 string `json:"nestkey1"`
NestValue2 string `json:"nestkey2"`
}
type ArrayItem struct {
ItemInfo1 string `json:"iteminfokey1"`
ItemInfo2 string `json:"iteminfokey2"`
}
func main() {
arrayItems := []ArrayItem{
ArrayItem{
ItemInfo1: "item info 1 array index 0",
ItemInfo2: "item info 2 array index 0",
},
ArrayItem{
ItemInfo1: "item info 1 array index 1",
ItemInfo2: "item info 2 array index 1",
},
}
nested := Nested{
NestValue1: "nest value 1",
NestValue2: "nest value 2",
}
jsonResponse := JSONResponse{
Value1: "value 1",
Value2: "value 2",
Nested: nested,
ArrayItems: arrayItems,
}
fmt.Printf("The struct returned before marshalling\n\n")
fmt.Printf("%+v\n\n\n\n", jsonResponse)
// The MarshalIndent function only serves to pretty print, json.Marshal() is what would normally be used
byteArray, err := json.MarshalIndent(jsonResponse, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Printf("The JSON response returned when the struct is marshalled\n\n")
fmt.Println(string(byteArray))
}
- 第12行:我們將ArrayItems添加到我們的JSONResponse圆裕,它是[] ArrayItem類型吓妆,ArrayItem類型的數(shù)組吨铸,我們將在下面聲明。
- 第20-23行:聲明ArrayItem結(jié)構(gòu)這是數(shù)組中每個(gè)項(xiàng)目所包含內(nèi)容的定義舟奠。
- 第27-36行:實(shí)例化類型為[] ArrayItem的arrayItem沼瘫,并填充每個(gè)索引中的值晕鹊。
- 第47行:在jsonResponse的聲明中引用arrayItems變量溅话。
測試它并在這里使用代碼https://play.golang.org/p/YhqKR6lZ_ge。
使用JSON
好的砚哆,現(xiàn)在讓我們看一下從其他API中使用JSON躁锁,我將從API中模擬返回的JSON以簡化該過程战转。由于我們現(xiàn)在已經(jīng)了解Go結(jié)構(gòu)如何與JSON字符串相關(guān)槐秧,因此我們將直接使用復(fù)雜的JSON對象。
這是我們將從API獲得的JSON響應(yīng)刁标。我正在使用從https://httpbin.org/get返回的JSON膀懈,這是一個(gè)用于測試HTTP請求和響應(yīng)的優(yōu)秀網(wǎng)站谨垃。這是我們將要收到的JSON乘客。(在終端運(yùn)行測試)curl [https://httpbin.org/get](https://httpbin.org/get)
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.54.0"
},
"origin": "83.7.252.17, 83.7.252.17",
"url": "https://httpbin.org/get"
}
請注意易核,“origin”對您來說會有所不同牡直,因?yàn)槟恼埱髞碜圆煌腎P碰逸±樱空的“args”對象將包含URL中傳遞的任何參數(shù)。所以https://httpbin.org/get?testArg=testValue&x=3會返回,
"args": {
"testArg": "testValue",
"x": "3"
}
當(dāng)我們不知道鍵名稱或值的類型將是什么時(shí)湃番,這個(gè)args值是一個(gè)很好的例子吠撮。為了解決這個(gè)問題泥兰,我們設(shè)置了“args”類型鞋诗,map[string]interface{}
它是一個(gè)帶有類型為string的鍵和類型為interface {}的值的映射(散列表)师脂〕跃空interface{}
是一種在Go中定義變量的方法酌心,因?yàn)椤斑@可能是任何東西”安券。在運(yùn)行時(shí)侯勉,Go將分配適當(dāng)?shù)膬?nèi)存以適合您決定存儲在其中的任何內(nèi)容址貌。
讓我們看一下在代碼中使用API??中的JSON并討論文件主要部分的示例练对。
package main
import (
"encoding/json"
"fmt"
)
type JSONResponse struct {
Args map[string]interface{} `json:"args"`
Headers Headers `json:"headers"`
Origin string `json:"origin"`
Url string `json:"url"`
}
type Headers struct {
Accept string `json:"Accept"`
Host string `json:"Host"`
UserAgent string `json:"User-Agent"`
}
func main() {
jsonString := `{"args": {}, "headers": {"Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.54.0"}, "origin": "89.7.222.10, 89.7.222.10", "url": "https://httpbin.org/get"}`
var jsonResponse JSONResponse
err := json.Unmarshal([]byte(jsonString), &jsonResponse)
if err != nil {
fmt.Println(err)
}
fmt.Println(jsonResponse)
}
- 第8-19行:聲明Structs以匹配返回的JSON的結(jié)構(gòu)。
- 第22行:類型為字符串的模擬JSON響應(yīng)它呀。
- 第24行:實(shí)例化JSONResponse類型的jsonResponse螺男。
- 第25行:從JSON字符串和jsonResponse變量棒厘,Go標(biāo)簽
json:"headers"
等中解組數(shù)據(jù),使特定的JSON鍵值能夠加載到結(jié)構(gòu)中的適當(dāng)變量中下隧。 - 第25行:unmarshal函數(shù)要求JSON數(shù)據(jù)在字節(jié)數(shù)組中傳遞绊谭,這就是我們將JSON字符串轉(zhuǎn)換為字節(jié)數(shù)組的原因。
- 第25行:unmarshal函數(shù)需要傳遞變量的指針汪拥,這就是為什么在jsonResponse之前有一個(gè)&符號达传。&jsonResponse是jsonResponse的內(nèi)存位置,請參閱此處以獲取有關(guān)指針的更多信息迫筑。
- 第27-29行:解組時(shí)的錯(cuò)誤檢查宪赶。
- 第31行:打印jsonResponse Go變量,該變量現(xiàn)在包含JSON字符串中的數(shù)據(jù)脯燃。
親自試試吧https://play.golang.org/p/oqFjZii_yjA。
解析接口
我們現(xiàn)在知道如果我們不確定要在JSON中使用的值類型或鍵名扁瓢,我們可以將JSON解析為map [string] interface {}類型。如果我們查看前面的示例并想象值是通過URL(https://httpbin.org/get?testArg=testValue&x=3)傳入的結(jié)果伟桅,
"args": {
"testArg": "testValue",
"x": "3"
}
只需通過引用jsonResponse.Args["testArg"]
和訪問這些值即可jsonResponse.Args["x"]
更扁∩蘅埃看看這里https://play.golang.org/p/WehVIkK8CRd相叁,我已經(jīng)更新了模擬的JSON以包含“args”對象中的值椿访,并在最后添加了2個(gè)打印語句,向您展示如何訪問值。
希望這有助于您更好地理解如何在Go中使用JSON钦勘。一如既往,歡迎任何反饋,如果我犯了任何錯(cuò)誤,請告訴我雹有。
翻譯自:https://medium.com/@ciaranmcveigh5/json-in-golang-an-introduction-8e889c29a83