如今前后端通信的数据格式基本都是 json, 因为 json 这种数据格式即简单又轻量。

在Go语言中,当我们需要进行 JSON 编码或解码时,我们可以使用标准库 encoding 包中的 json 包。

json 包为我们提供了一系列的方法进行 json 编解码,比如解析时使用的 NewDecoder() 和 Decode() 方法,以及将结构类型或 map 类型编码为 json 字符串的 Marshal() 方法,解析 json 字符串的 Unmarshal() 方法。

接下来,让我们逐个看如何使用这些方法。

JSON 解析到结构类型

当我们拿到一份 json 数据,然后想要将 json 数据解析到结构类型中的时候,可以使用 json 包中的 NewDecoder() 和 Decode() 这两个函数。

package main
​
import (
  "encoding/json"
  "fmt"
  "net/http"
)
​
​
type res struct {
  Data []Item `json:"data"`
}
​
type Item struct {
  Ename string `json:"ename"`
  Name string `json:"name"`
  Nid int `json:"nid"`
}
​
func main() {
  var res res
  url := "https://studygolang.com/nodes/hot?limit=10"
  // 调用 http 包的 Get() 方法请求 json 数据
  resp, err := http.Get(url)
  if err != nil {
    fmt.Print("请求出错:", err)
  }
​
  defer resp.Body.Close()
​
  // 解析 resp.Body 数据到 res 结构类型变量中
  json.NewDecoder(resp.Body).Decode(&res)
​
  fmt.Printf("结果:%+vn", res)
}

结果:

可以看到我们已经将请求到的 json 数据解析到了 res 这个结构类型的变量中。

此时我们可以很方便的访问 res 中的字段。

first := res.Data[0]
fmt.Println(first.Name, first.Ename, first.Nid)

结果:

除了解析到结构类型变量中,我们还可以解析到 map 类型中。

JSON 解析到 Map 类型

package main
​
import (
  "encoding/json"
  "fmt"
  "net/http"
)
​
func main() {
  url := "https://studygolang.com/nodes/hot?limit=10"
  // 调用 http 包的 Get() 方法请求 json 数据
  resp, err := http.Get(url)
  if err != nil {
    fmt.Print("请求出错:", err)
  }
​
  defer resp.Body.Close()
​
  var res map[string]interface{}
​
  json.NewDecoder(resp.Body).Decode(&res)
​
  fmt.Printf("结果:%+vn", res)
}

结果:

当然直接解析到 map 中其实很不方便,因为你想要获取map 中某个值是很麻烦的事。

JSON 字符串解析

如果拿到的是一份 json 字符串,那应该如何解析JSON 字符串呢?

Go 中,我们可以使用 Unmarshal() 这个方法进行解析 json 字符串到结构类型或者Map 类型中。

package main
​
import (
  "encoding/json"
  "fmt"
)
​
type Res struct {
  Name string `json:"name"`
  Title string `json:"title"`
  Contact struct{
    City string `json:"city"`
    Phone string `json:"phone"`
  } `json:"contact"`
}
​
func main() {
  var JSON = `{
    "name": "Gopher",
    "title": "programmer",
    "contact": {
      "city": "beijing",
      "phone": "19999999999"
    }
  }`
​
  var res Res
  json.Unmarshal([]byte(JSON), &res)
  fmt.Printf("结果:%+vn", res)
}

结果:

需要注意的是,Unmarshal() 接收的第一个参数是[ ]byte,所以必须先将 json 字符串转为 [ ]byte 类型。

接下来可以正常访问 res 中的字段了。

fmt.Println(res.Name, res.Contact, res.Contact.City)

结果:

当然,也可以解析到 map 类型中,如下

package main
​
import (
  "encoding/json"
  "fmt"
)
​
func main() {
  var JSON = `{
    "name": "Gopher",
    "title": "programmer",
    "contact": {
      "city": "beijing",
      "phone": "19999999999"
    }
  }`
​
  var res map[string]interface{}
  json.Unmarshal([]byte(JSON), &res)
  fmt.Printf("结果:%+vn", res)
}
​

结果:

访问map 中的字段

fmt.Println(res["name"], res["contact"])

结果:

map 第一层字段能正常访问,但如果想要访问 map 第二层以上的就麻烦了,我们先尝试访问,看看有什么结果。

fmt.Println(res["contact"]["city"])

输入以上代码,代码还没执行,已经提示错误。

它说 res["contact"] 是一个 interface{} 的类型,不支持索引。因为我们解析json 的时候,因为 json 中的字段值类型可能是多样的,不能写死,所以定义为 interface{} 空接口类型来接收。

如果我们要访问 res["contact"] 中的字段,那我们必须先将 res["contact"] 转换为具体的类型。

我们使用类型断言类将 res["contact"] 转换为 map[string]interface{} 类型,现在我们就可以正常访问 res["contact"] 中的字段了。

fmt.Println(res["contact"].(map[string]interface{})["city"])

结果:

虽然可以访问 map 中第二层以上的数据了,但是如果层级一多,每一层都要使用类型断言来进行转换,那会很不方便。所以,如果 json 数据嵌套复杂的话,还是不建议使用解析到 map 中的方式处理数据。

JSON 编码

所谓 json 编码,就是将数据转化为 json 字符串,比如将结构类型或map类型转化为 json 字符串,也叫序列化。

将结构类型和 map 类型转化为 json 字符串,我们可以使用 json 包提供的 Marshal() 或 MarshalIndent() 这两个方法进行编码。

package main
​
import (
  "encoding/json"
  "fmt"
)
​
func main() {
  var JSON = `{
    "name": "Gopher",
    "title": "programmer",
    "contact": {
      "city": "beijing",
      "phone": "19999999999"
    }
  }`
​
  var res map[string]interface{}
  json.Unmarshal([]byte(JSON), &res)
​
  // JSON 编码
  jsonStr, err := json.Marshal(res)
​
  if err == nil {
    fmt.Printf("结果:%+vn", string(jsonStr))
  }
​
  jsonStr2, err2 := json.MarshalIndent(res, "前缀", "  ")
  if err2 == nil {
    fmt.Printf("结果2:%+vn", string(jsonStr2))
  }
}
​

可以看到,通过调用 json.Marshal() 方法或 json.MarshalIndent() 方法就可以把 map 类型或结构类型转化为 json 字符串。

这两个方法都是返回两个值,第一个是 [ ]byte 类型,第二个参数是 error 类型,所以如果想要查看转化后的结果,需要将 [ ]bype 类型转为 string 类型,直接使用 string() 转化即可。

这两个方法不同在于,json.MarshalIndent() 可以设置前缀或缩进,可以通过第二和第三个参数设置。

总结

  • NewDecoder() 和 Decode() 用于解析 json 到结构类型或map类型
  • Unmarshal() 用于解析 json 字符串
  • Marshal() 或 MarshalIndent() 用于将结构类型或map类型解码为 json 字符串

以上就是本文所分享的全部内容,希望对你有所帮助。

关注公众号:陆贵成,一起精进Golang技术