如今前后端通信的数据格式基本都是 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技术