Golang中的JSON簡介

介紹

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

}

return_json.go

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

}

return_json_with_nested.go

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

}

return_json_with_array.go

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

}

consume_json.go

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容