Go中(JSON)编码/解码

492 阅读4分钟

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语言实战》