1

Go语言解析JSON

 1 year ago
source link: https://www.yangyanxing.com/article/2022-08-12go%E8%AF%AD%E8%A8%80%E8%A7%A3%E6%9E%90json.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文将说明如何利用 Go 语言将 JSON 解析为结构体和数组,如果解析 JSON 的嵌入对象,如何将 JSON 的自定义属性名称映射到结构体,如何解析非结构化的 JSON 字符串。

JSON 解析为结构体

JSON 的结构是 key-value,最直观的就是将 JSON 解析为结构体,如下 JSON :

{
  "name": yuzhou1u,
  "age": 18
}

Go 语言中,提供了一个专门的包 encoding/json ,所以我们在使用这个 JSON 包之前需要在头文件导入:

package main

import (
  "encoding/json"
  "fmt"
)

然后,我们需要定义一个 Go 语言的结构体以便我们能与 JSON 一一对应,比如在 JSON 中我们定义了姓名 name 和年龄 age ,所以需要定义一个结构体(命名可以随意,但最好通俗易懂)的字段与 JSON 字符串中的键相匹配:

type Person struct {
  Name string
  Age int
}

然后使用 json.Umarshal() 函数来解析 JSON 字符串,完整代码如下:

package main

import (
  "encoding/json"
  "fmt"
)

type Person struct {
  Name string
  Age  int
}

func main() {
  var p Person

  jsonString := `{"name": "yuzhou1su",
          "age" : 18}`

  err := json.Unmarshal([]byte(jsonString), &p)

  if err == nil {
    fmt.Println(p.Name)
    fmt.Println(p.Age)
  } else {
    fmt.Println(err)
  }

}

现在来解释一下上面 main 函数的代码:

定义一个 Person 的 p 对象

因为我们没有把文件系统使用上,所以是定义了一个 jsonString的 JSON 数据

使用 json.Unmarshal() 函数能够解析 JSON 格式的数据。但需要将 JSON 字符串转换为字节切片,并将结果存储到 p 对象中。使用需要使用 & 地址运算符传入人员的地址。

如果解析有效,则 json.Unmarshal() 函数返回 nil,您现在可以找到存储在 person 变量中的值。

确保将 Person 结构中每个字段的第一个字符大写。如果字段名称以小写字母开头,则不会导出到当前包之外,并且字段对 json.Unmarshal() 函数不可见。

运行上述代码,打印在控制台中结果为:

yuzhou1su 18

JSON 解析为数组

通常 JSON 数据会包括一系列的对象数组,就像这样一个班级的数据:

[
  {
    "id": 1,
    "name": "张三"
    "age": 20
  },
  {
    "id": 2,
    "name": "李翠花"
    "age": 18
  },
  {
    "id": 3,
    "name": "王老五"
    "age": 25
  }
]

我们只需要定义一个 students[] 的数组,代码如下:

package main

import (
  "encoding/json"
  "fmt"
)

type Student struct {
  Id   int
  Name string
  Age  int
}

func main() {

  var students []Student
  myClass :=
    `[
      {
        "id": 1,
        "name": "张三",
        "age": 20
      },
      {
        "id": 2,
        "name": "李翠花",
        "age": 18
      },
      {
        "id": 3,
        "name": "王老五",
        "age": 25
      }
  ]`

  err := json.Unmarshal([]byte(myClass), &students)

  if err == nil {
    for _, student := range students {
      fmt.Print("\t\n", student.Id)
      fmt.Print("\t", student.Name)
      fmt.Print("\t", student.Age)
    }
  } else {
    fmt.Println(err)
  }
}

使用 for...range 迭代数组,然后运行上述代码:

$ go run main.go

1  张三  20
2  李翠花  18
3  王老五  25

解析 JSON 嵌入对象

JSON 字符串有时包含嵌入对象,比如:

{
  "name": "yuzhou1su",
  "age": 18,
  "address": {
    "road": "renmin south road",
    "street": "123 street",
    "city": "cs",
    "province": "hn",
    "country": "cn"
  }
}

address 就是属于内嵌对象,我们同样需要创建另一个 Address 结构体:

package main

import (
  "encoding/json"
  "fmt"
)

type Person struct {
  Name    string
  Age     int
  Address struct {
    Road     string
    Street   string
    City     string
    Province string
    Country  string
  }
}

func main() {
  var p Person

  jsonString := `
        {
            "name": "yuzhou1su",
            "age": 18,
            "address": {
              "road": "renmin south road",
              "street": "123 street",
              "city": "cs",
               "province": "hn",
              "country": "cn"
            }
        }`

  err := json.Unmarshal([]byte(jsonString), &p)

  if err == nil {
    fmt.Println(p.Name)
    fmt.Println(p.Age)
    fmt.Println(p.Address.Road)
    fmt.Println(p.Address.Street)
    fmt.Println(p.Address.City)
    fmt.Println(p.Address.Province)
    fmt.Println(p.Address.Country)
  } else {
    fmt.Println(err)
  }

}

输出结果:

yuzhou1su
18
renmin south road
123 street
cs
hn
cn

自定义属性名称的映射

有时 JSON 字符串中的键不能直接映射到 Go 中结构的成员。比如:

{
  "base currency": "USD",
  "destination currency": "CNY"
}

请注意,此 JSON 字符串中的键中有空格。如果你尝试将它直接映射到一个结构,你会遇到问题,因为 Go 中的变量名不能有空格。要解决此问题,您可以使用结构字段标记(在结构中的每个字段之后放置的字符串文字),如下所示:

type Rates stuct {
  Base string `json:"base currency"`
  Symbol string `json:"destination currency"`
}

JSON 的 base currency 映射到 Go 中的 Base 字段

JSON 的 destination currency 映射到 Go 中 Symbol

整合如下:

package main

import (
  "encoding/json"
  "fmt"
)

type Rates struct {
  Base   string `json:"base currency"`
  Symbol string `json:"destination currency"`
}

func main() {

  jsonString := `
        {
            "base currency": "USD",
            "destination currency": "CNY"
        }`

  var rates Rates
  err := json.Unmarshal([]byte(jsonString), &rates)

  if err == nil {
    fmt.Println(rates.Base)
    fmt.Println(rates.Symbol)
  } else {
    fmt.Println(err)
  }

}

运行如下代码:

$ go run main.go
USD
CNY

非结构化数据的映射

前面几节展示了相对简单的 JSON 字符串。然而,在现实世界中,您要操作的 JSON 字符串通常很大且非结构化。此外,您可能只需要从 JSON 字符串中检索特定值。

考虑以下 JSON 字符串:

{
    "success": true,
    "timestamp": 1588779306,
    "base": "USD",
    "date": "2022-01-15",
    "rates": {
        "BNB": 0.00225,
        "BTC": 0.000020,
        "EUR": 0.879,
        "GBP": 0.733,
        "CNY": 6.36
    } 
}

如果我们还想把美元解析为其他币种,不至于重新定义整个结构体,可以采取定义一个接口:

var result map[string] interface{}

上面的语句创建了一个map类型的变量 result,它的 key 是string 类型,每个对应的 value 都是 interface{} 类型。这个空接口表示该值可以是任何类型:

为了解析这个 JSON 字符串,我们应该使用 json.Unmarshal() 函数:

json.Unmarshal([]byte(jsonString), &result)

因为 result 的类型是接口,所以可以传入任何类型:

当解析 success 键的话可以使用 result["sucess"],解析为布尔型。

当解析 timestamp 时可以解析为数字类型

解析 rates 使用传入 rates 即可, 即 rates := result["rates"],解析为 map 类型

整个代码如下:

package main

import (
  "encoding/json"
  "fmt"
)

type Rates struct {
  Base   string `json:"base currency"`
  Symbol string `json:"destination currency"`
}

func main() {

  jsonString := `
        {
            "success": true,
            "timestamp": 1588779306,
            "base": "USD",
            "date": "2022-01-15",
            "rates": {
                "BNB": 0.00225,
                "BTC": 0.000020,
                "EUR": 0.879,
                "GBP": 0.733,
                "CNY": 6.36
          } 
        }`

  var result map[string]interface{}
  err := json.Unmarshal([]byte(jsonString), &result)
  if err == nil {
    fmt.Println(result["success"])
    rates := result["rates"]
    fmt.Println(rates)
  } else {
    fmt.Println(err)
  }

}

运行代码如下:

$ go run main.go
true
map[BNB:0.00225 BTC:2e-05 CNY:6.36 EUR:0.879 GBP:0.733]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK