介绍

在本文中,我将向您展示如何在Go中解析和返回JSON的基础知识,同时还向您提供对该过程的理解。

首先,让我们看一下如何在其他语言中解析JSON,特别是动态类型的,我将以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.dumps(p_dict)json.loads(j_str)

现在,在像Go这样的静态类型语言中解析像JSON这样的格式会带来一些问题。Go是静态类型的,这意味着程序中每个变量的类型都需要在编译时知道。这意味着您作为程序员必须指定每个变量的类型。其他语言(python)提供某种形式的类型推断,类型系统能够推断出变量的类型。静态类型语言的主要优点是所有类型的检查都可以由编译器完成,因此在很早的阶段就会发现许多琐碎的错误。一个简单的代码示例可能会澄清。

PYTHON
x = 3
GOLANG 
var x int = 3 

在解析JSON时,任何东西都可以显示在JSON主体中,那么编译器如何知道如何设置内存,因为它不知道类型?

这有两个答案。当你知道你的数据会是什么样子时会得到一个答案,而当你不了解数据时会得到答案。我们将首先介绍第一个选项,因为这是最常见的情况并导致最佳实践。

当JSON中的数据类型已知时,您应该将JSON解析为您定义的结构。任何不适合结构的字段都将被忽略。我们将在返回和使用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))

}
json: "key1"

请在此处运行代码以查看结果,使用代码也是学习https://play.golang.org/p/gaBMvz21LiA的最佳方式。

嵌套的JSON

现在让我们看一下具有嵌套项的更复杂的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类型,这是我们即将创建的类型。
  • 第14-17行:我们声明了我们在JSONResponse中使用的嵌套类型结构,遵循我们想要返回的JSON的格式。
  • 第21-30行:我们实例化Nested类型的嵌套变量和JSONResponse类型的jsonResponse变量,后者又引用我们刚刚声明的嵌套变量。

这些是这个例子和前一个例子之间的主要区别。再次运行此处的代码进行测试,并在评论https://play.golang.org/p/GcRceKe1jC-中进行一些修改。

JSON中的数组

最后,让我们看一下返回包含数组的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"
    }
  ]
}

下面的代码显示了这是如何完成的,再次让我们讨论一下发生了什么变化。

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类型的数组,我们将在下面声明。
  • 第20-23行:声明ArrayItem结构这是数组中每个项目所包含内容的定义。
  • 第27-36行:实例化类型为[] ArrayItem的arrayItem,并填充每个索引中的值。
  • 第47行:在jsonResponse的声明中引用arrayItems变量。
使用JSON

好的,现在让我们看一下从其他API中使用JSON,我将从API中模拟返回的JSON以简化该过程。由于我们现在已经了解Go结构如何与JSON字符串相关,因此我们将直接使用复杂的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。空的“args”对象将包含URL中传递的任何参数。所以https://httpbin.org/get?testArg=testValue&x=3会返回,

"args": {
  "testArg": "testValue",
  "x": "3"
}
map[string]interface{}interface{}

让我们看一下在代码中使用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)

}
json:"headers"
解析接口

我们现在知道如果我们不确定要在JSON中使用的值类型或键名,我们可以将JSON解析为map [string] interface {}类型。如果我们查看前面的示例并想象值是通过URL(https://httpbin.org/get?testArg=testValue&x=3)传入的结果,

"args": {
  "testArg": "testValue",
  "x": "3"
}
jsonResponse.Args["testArg"]jsonResponse.Args["x"]

希望这有助于您更好地理解如何在Go中使用JSON。一如既往,欢迎任何反馈,如果我犯了任何错误,请告诉我。