Golang中的JSON簡介

介紹

在本文中,我將向您展示如何在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))

}

return_json.go

  • 第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))

}

return_json_with_nested.go

  • 第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))

}

return_json_with_array.go

  • 第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)

}

consume_json.go

  • 第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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末质帅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子魄揉,更是在濱河造成了極大的恐慌瓣俯,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袜漩,死亡現(xiàn)場離奇詭異,居然都是意外死亡仇味,警方通過查閱死者的電腦和手機(jī)嬉愧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門裕便,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缤言,你說我怎么就攤上這事郑口≌袄耄” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長寝杖,這世上最難降的妖魔是什么只盹? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮肛冶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伦乔。我一直安慰自己皿淋,他們只是感情好哑舒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天济赎,我揣著相機(jī)與錄音壳猜,去河邊找鬼闪幽。 笑死腕够,一個(gè)胖子當(dāng)著我的面吹牛大诸,可吹牛的內(nèi)容都是我干的贿堰。 我是一名探鬼主播纵搁,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼眼耀,長吁一口氣:“原來是場噩夢啊……” “哼楞黄!你這毒婦竟也來了碎税?” 一聲冷哼從身側(cè)響起责蝠,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤值朋,失蹤者是張志新(化名)和其女友劉穎丰辣,沒想到半個(gè)月后芽隆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腕扶,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遂鹊,我是刑警寧澤舟陆,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布茎活,位于F島的核電站身辨,受9級特大地震影響吏饿,放射性物質(zhì)發(fā)生泄漏笨忌。R本人自食惡果不足惜途凫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一欣硼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧累魔,春花似錦命辖、人聲如沸味廊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽如蚜。三九已至,卻和暖如春讨勤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茂契。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工讨韭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秉撇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跨新。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容