1. 前言
如果程序需要处理 JSON,可以使用标准库里名为json 的包,它们可以处理这些格式的数据。
2. 解码json
package jsonlearn
import (
"encoding/json"
"fmt"
"net/http"
"testing"
)
type JsonData struct {
UserId int `json:"userId"`
Id int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
func TestDecode(t *testing.T) {
//通过HTTP Get调用获得一个json
// 返回值(resp *http.Response)类型
resp, err := http.Get("https://jsonplaceholder.typicode.com/todos/1")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
var j JsonData
//我们使用`json.NewDecoder()`函数和`Decode()`方法将响应体中的JSON数据解码到`JsonData`类型的变量`j`中
err = json.NewDecoder(resp.Body).Decode(&j)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("gr => ", gr)
}
我们使用json.NewDecoder()函数和Decode()方法将响应体中的JSON数据解码到JsonData类型的变量j中
输出结果
gr => {1 1 delectus aut autem false}
注意 JsonData结构体中字段后的这些字符串被称作标签(tag)
是提供每个字段的元信息 的一种机制,将 JSON 文档和结构类型里的字段一一映射起来。如果不存在标签,编码和解码过 程会试图以大小写无关的方式,直接使用字段的名字进行匹配。如果无法匹配,对应的结构类型 里的字段就包含其零值
`json:"userId"`
2.1 NewDecoder 和 Decode
golang.org/src/encoding/json/stream.go
// NewDecoder 返回从 r 读取的解码器
//
// 解码器自己会进行缓冲,而且可能会从 r 读比解码 JSON 值
// 所需的更多的数据
func NewDecoder(r io.Reader) *Decoder
// Decode 从自己的输入里读取下一个编码好的 JSON 值,
// 并存入 v 所指向的值里
//
// 要知道从 JSON 转换为 Go 的值的细节,
// 请查看 Unmarshal 的文档
func (dec *Decoder) Decode(v interface{}) error
函数 NewDecoder 返回一个指向 Decoder 类型的指针值。由于 Go 语言支持复合语句调用, 可以直接调用从 NewDecoder 函数返回的值的 Decode 方法,而不用把这个返回值存入变量。 在代码清单 8-25 里,可以看到 Decode 方法接受一个 interface{}类型的值做参数,并返回 一个 error 值。
任何类型都实现了一个空接口 interface{}。这意味着 Decode 方法可以接受任意类型的值。使用反射,Decode 方法会拿到传入值的类型信息。然后,在读取 JSON 响应的过程中,Decode 方法会将对应的响应解码为这个类型的值。这意味着用户不需要创建对 应的值,Decode 会为用户做这件事情,
我们 向 Decode 方法传入了指向 JsonData 类型的指针变量的地址, 而这个地址的实际值为 nil。该方法调用后,这个指针变量会被赋给一个 JsonData 类型的值, 并根据解码后的 JSON 文档做初始化。
var j JsonData
err = json.NewDecoder(resp.Body).Decode(&j)
2.2 Unmarshal 解析Json
有时,无法为 JSON 的格式声明一个结构类型,而是需要更加灵活的方式来处理 JSON 文档。 在这种情况下,可以将 JSON 文档解码到一个 map 变量中
package jsonlearn
import (
"encoding/json"
"fmt"
"log"
"testing"
)
// JSON 包含要反序列化的样例字符串
var JSON = `{
"name": "Gopher",
"title": "programmer",
"contact": {
"home": "415.333.3333",
"cell": "415.555.5555"
}
}`
func TestUnmarshal(t *testing.T) {
// 将 JSON 字符串反序列化到 map 变量
var c map[string]interface{}
err := json.Unmarshal([]byte(JSON), &c)
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println("Name:", c["name"])
fmt.Println("Title:", c["title"])
fmt.Println("Contact")
fmt.Println("H:", c["contact"].(map[string]interface{})["home"])
fmt.Println("C:", c["contact"].(map[string]interface{})["cell"])
}
3. 编码json
我们要学习的处理 JSON 的第二个方面是,使用 json 包的 MarshalIndent 函数进行编码。 这个函数可以很方便地将 Go 语言的 map 类型的值或者结构类型的值转换为易读格式的 JSON 文 档。序列化(marshal)是指将数据转换为 JSON 字符串的过程。下面是一个将 map 类型转换为 JSON 字符串的例子
3.1 MarshalIndent
package jsonlearn
import (
"encoding/json"
"fmt"
"log"
"testing"
)
func TestMarshalIndent(t *testing.T) {
// 创建一个保存键值对的映射
c := make(map[string]interface{})
c["name"] = "Gopher"
c["title"] = "programmer"
c["contact"] = map[string]interface{}{
"home": "415.333.3333",
"cell": "415.555.5555",
}
// 将这个映射序列化到 JSON 字符串
data, err := json.MarshalIndent(c, "", " ")
if err != nil {
log.Println("ERROR:", err)
return
}
fmt.Println(string(data))
}
// MarshalIndent 很像 Marshal,只是用缩进对输出进行格式化
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
函数 MarshalIndent 会使用反射来确定如何将 map 类型转换为 JSON 字符串。
3.2 Marshal
func Marshal(v any) ([]byte, error) {
json 包还提供了名为 Marshal 的函数来进行解码。这个函数产生的 JSON 字符串很适合作为在网络响应(如 Web API)的数据。函数 Marshal 的工作原理和函数 MarshalIndent 一样,只不过没有用于前缀 prefix 和缩进 indent 的参数
参考书籍《Go语言实战》