
介紹
在本文中,我將向您展示如何在Go中解析和返回JSON的基礎知識,同時還向您提供對該過程的理解。
首先,讓我們看一下如何在其他語言中解析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的情況下,我們有一個python字典“p_dict”,我們可以使用它轉(zhuǎn)換為JSON字符串json.dumps(p_dict)。當使用JSON時,我們有一個JSON字符串“j_str”,我們可以使用它轉(zhuǎn)換為python diction json.loads(j_str)。
現(xiàn)在,在像Go這樣的靜態(tài)類型語言中解析像JSON這樣的格式會帶來一些問題。Go是靜態(tài)類型的,這意味著程序中每個變量的類型都需要在編譯時知道。這意味著您作為程序員必須指定每個變量的類型。其他語言(python)提供某種形式的類型推斷,類型系統(tǒng)能夠推斷出變量的類型。靜態(tài)類型語言的主要優(yōu)點是所有類型的檢查都可以由編譯器完成,因此在很早的階段就會發(fā)現(xiàn)許多瑣碎的錯誤。一個簡單的代碼示例可能會澄清。
PYTHON
x = 3
GOLANG
var x int = 3
在解析JSON時,任何東西都可以顯示在JSON主體中,那么編譯器如何知道如何設置內(nèi)存,因為它不知道類型?
這有兩個答案。當你知道你的數(shù)據(jù)會是什么樣子時會得到一個答案,而當你不了解數(shù)據(jù)時會得到答案。我們將首先介紹第一個選項,因為這是最常見的情況并導致最佳實踐。
當JSON中的數(shù)據(jù)類型已知時,您應該將JSON解析為您定義的結(jié)構(gòu)。任何不適合結(jié)構(gòu)的字段都將被忽略。我們將在返回和使用JSON時探索此選項。

返回JSON
假設我們想要返回以下JSON對象。
{
"key1": "value 1",
"key2": "value 2"
}
下面的代碼顯示了這是如何完成的,讓我們來談談它。
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行:我們導入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)的實例化并填充值。
- 第20-21行:我們將jsonResponse變量打印到控制臺,以便我們可以將其與稍后打印的JSON進行對比。
- 第23-24行:我們將變量jsonResponse編組為JSONResponse類型,將其轉(zhuǎn)換為字節(jié)數(shù)組并考慮“Go Tags”中聲明的映射。返回的字節(jié)數(shù)組存儲在byteArray變量中。
- 第26-28行:我們對Marshal函數(shù)進行錯誤檢查,如果不是nil則打印錯誤。
- 第30-31行:我們將byteArray轉(zhuǎn)換為字符串并打印它,我們現(xiàn)在可以查看打印結(jié)構(gòu)和打印JSON之間的區(qū)別。
請在此處運行代碼以查看結(jié)果,使用代碼也是學習https://play.golang.org/p/gaBMvz21LiA的最佳方式。
嵌套的JSON
現(xiàn)在讓我們看一下具有嵌套項的更復雜的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行:我們實例化Nested類型的嵌套變量和JSONResponse類型的jsonResponse變量,后者又引用我們剛剛聲明的嵌套變量。
這些是這個例子和前一個例子之間的主要區(qū)別。再次運行此處的代碼進行測試,并在評論https://play.golang.org/p/GcRceKe1jC-中進行一些修改。
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ù)組中每個項目所包含內(nèi)容的定義。
- 第27-36行:實例化類型為[] ArrayItem的arrayItem,并填充每個索引中的值。
- 第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),因此我們將直接使用復雜的JSON對象。
這是我們將從API獲得的JSON響應。我正在使用從https://httpbin.org/get返回的JSON,這是一個用于測試HTTP請求和響應的優(yōu)秀網(wǎng)站。這是我們將要收到的JSON。(在終端運行測試)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”對您來說會有所不同,因為您的請求來自不同的IP??盏摹癮rgs”對象將包含URL中傳遞的任何參數(shù)。所以https://httpbin.org/get?testArg=testValue&x=3會返回,
"args": {
"testArg": "testValue",
"x": "3"
}
當我們不知道鍵名稱或值的類型將是什么時,這個args值是一個很好的例子。為了解決這個問題,我們設置了“args”類型,map[string]interface{}它是一個帶有類型為string的鍵和類型為interface {}的值的映射(散列表)???code>interface{}是一種在Go中定義變量的方法,因為“這可能是任何東西”。在運行時,Go將分配適當?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響應。
- 第24行:實例化JSONResponse類型的jsonResponse。
- 第25行:從JSON字符串和jsonResponse變量,Go標簽
json:"headers"等中解組數(shù)據(jù),使特定的JSON鍵值能夠加載到結(jié)構(gòu)中的適當變量中。 - 第25行:unmarshal函數(shù)要求JSON數(shù)據(jù)在字節(jié)數(shù)組中傳遞,這就是我們將JSON字符串轉(zhuǎn)換為字節(jié)數(shù)組的原因。
- 第25行:unmarshal函數(shù)需要傳遞變量的指針,這就是為什么在jsonResponse之前有一個&符號。&jsonResponse是jsonResponse的內(nèi)存位置,請參閱此處以獲取有關(guān)指針的更多信息。
- 第27-29行:解組時的錯誤檢查。
- 第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個打印語句,向您展示如何訪問值。
希望這有助于您更好地理解如何在Go中使用JSON。一如既往,歡迎任何反饋,如果我犯了任何錯誤,請告訴我。
翻譯自:https://medium.com/@ciaranmcveigh5/json-in-golang-an-introduction-8e889c29a83