GolangGo 语言 JSON 的序列化与反序列化实践
Posted Fxtack
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GolangGo 语言 JSON 的序列化与反序列化实践相关的知识,希望对你有一定的参考价值。
Go 语言 JSON 的序列化与反序列化实践
导读
本文使用 Go 原生支持的包,对 JSON 字符串以及 .json
文件进行序列化与反序列化实践。
使用到的包是 encoding/json
。详细文档可查看官方中文文档,链接为: https://studygolang.com/pkgdoc
本文中所有代码块的 Go 代码均经过运行测试,可以直接复制运行。
请留意代码块中的注释,注释中有表示出一些非常重要的细节!
文章目录
一. 简述序列化与反序列化
序列化(Serialization),有的人也称之为反解析。指的是将对象的状态信息转换为可以存储或传输的形式的过程。
而反序列化,有的人成为解析。指的就是序列化的逆过程。
图1. 序列化与反序列化
例如,定义如下结构体 book,我们可以创建一个实体对象,bookObj,此时 bookObj 是一个在程序中的实体对象。若我要存储这个对象,或者将其通过 HTTP 进行传输,我就需要将其进行序列化为可以进行存储或者传输的文件形式,例如 json。
package main
import (
"fmt"
"time"
)
type Book struct {
Name string
Author string
PublishTime time.Time
}
func main() {
bookObj := &Book{
Name: "Go 语言基础入门",
Author: "fxtack",
PublishTime: time.Now(),
}
fmt.Println(bookObj)
}
若进行序列化,序列化后的 bookObj 对象的 JSON 格式为(注意,结构体字段名与 JSON 中的字段名并不是完全相等的):
{
"name": "Go 语言基础入门",
"author": "fxtack",
"publishTime": "2021-07-03T18:23:57.7451495+08:00"
}
这样我就可以通过存储和传输 json 来实现对 bookObj 对象的存储和传输了。
相应的,如果需要这个对象而读取 json 文件,或者传输的接收者收到这个对象的 json 并打算使用这个对象时,就需要通过反序列化,将其从 json 的格式重新转化为程序中的对象。
二. JSON 的序列化
1. 序列化简单示例
先写一个简单测试,示例如何使用 Go 进行序列化。代码如下:
package main
import (
"encoding/json"
"fmt"
"time"
)
type Book struct {
Name string
Author string
PublishTime time.Time
}
func main() {
bookObj := &Book{
Name: "Go 语言基础入门",
Author: "fxtack",
PublishTime: time.Now(),
}
// 使用 encoding/json 包进行序列化
// json.Marshal 序列化函数可以接受对象实例也可以接受对象指针
// 返回 byte[] 与 error,使用 error 可以对序列化是否成功进行判断,此处省略
jsonBytes, _ := json.Marshal(bookObj)
// 以字符串形式将序列化结果输出
fmt.Println(string(jsonBytes))
}
结果如下,已经符合 JSON 格式,序列化成功。
{"Name":"Go 语言基础入门","Author":"fxtack","PublishTime":"2021-07-03T19:00:04.1716455+08:00"}
注意
结构体中每个字段首字母必须是大写,这样各个字段才是可被访问的,也才能顺利完成序列化。下文中的反序列化也同理。
我们可以注意到,虽然输出的字符串已经符合 JSON 的语法要求,但其格式还不够规范。Go 中还有一个序列化函数为 json.MarshalIndent
可以输出更好的格式。使用如下:
package main
import (
"encoding/json"
"fmt"
"time"
)
type Book struct {
Name string
Author string
PublishTime time.Time
}
func main() {
bookObj := &Book{
Name: "Go 语言基础入门",
Author: "fxtack",
PublishTime: time.Now(),
}
// 注意 json.MarshalIndent 的参数多了两个
jsonBytes, _ := json.MarshalIndent(bookObj, "", "\\t")
fmt.Println(string(jsonBytes))
}
此时输出为规范的 JSON 内容,如下:
{
"Name": "Go 语言基础入门",
"Author": "fxtack",
"PublishTime": "2021-07-03T19:07:19.8107909+08:00"
}
此时我们可以看到 JSON 内容中的字段与结构体中的字段名是完全相同的。在本文第一章中展示的 JSON 内容则是首字母小写的驼峰命名法。
若要将结构体字段映射到新的名称,可以在结构体中使用 json 标签来实现。
package main
import (
"encoding/json"
"fmt"
"time"
)
type Book struct {
// 注意,每个字段都使用了对 json 标签,标签中的内容将会映射为 JSON 的字段名
Name string `json:"name"`
Author string `json:"author"`
PublishTime time.Time `json:"publishTime"`
}
func main() {
bookObj := &Book{
Name: "Go 语言基础入门",
Author: "fxtack",
PublishTime: time.Now(),
}
jsonBytes, _ := json.MarshalIndent(bookObj, "", "\\t")
fmt.Println(string(jsonBytes))
}
结果如下:
{
"name": "Go 语言基础入门",
"author": "fxtack",
"publishTime": "2021-07-04T15:18:45.9907985+08:00"
}
如果需要,可以通过 json 标签映射为任意需要的名称。
2. 序列化完整代码
接下来考虑完整的过程,将对象序列化为 .json
文件,其流程如下:
代码如下:
package main
import (
"encoding/json"
"log"
"os"
"time"
)
type Book struct {
// 使用 json 标签
Name string `json:"name"`
Author string `json:"author"`
PublishTime time.Time `json:"publishTime"`
}
func main() {
bookObj := &Book{
Name: "Go 语言基础入门",
Author: "fxtack",
PublishTime: time.Now(),
}
// 调用序列化函数,序列化结果存储到 ./book.json
WriteJson("./book.json", bookObj)
}
// 序列化为 .json 文件函数
// filePath 为存储到的路径,ref 为需要序列化的对象或其应用
func WriteJson(filePath string, ref interface{}) {
// 判断提供的路径文件是否存在,避免直接覆盖已有的文件
f, err := os.Stat(filePath)
if (err == nil || os.IsExist(err)) && !f.IsDir(){
log.Println("文件已经存在或存在同名文件夹")
return
}
// 创建 .json 文件
file, err := os.Create(filePath)
if err != nil {log.Println("文件创建失败")}
defer file.Close()
// 序列化
json, err := json.MarshalIndent(ref, "", "\\t")
if err != nil {
log.Println("序列化失败")
}
// 写入 .json 文件
file.Write(json)
}
运行后会在 Go 模块下多出一个 book.json
的文件,说明序列化成功。
二. JSON 的反序列化
1. 反序列化简单示例
先写一个简单测试,演示 Go 是如何完成 JSON 的反序列化。
这里用我们在上文中序列化得到的 JSON 字符串来做演示。
package main
import (
"encoding/json"
"fmt"
"time"
)
type Book struct {
// 注意,使用了 json 标签,则 JSON 内容的字段名必须与 json 标签的值一致才能被解析
Name string `json:"name"`
Author string `json:"author"`
PublishTime time.Time `json:"publishTime"`
}
func main() {
// 序列化示例中得到的 JSON 字符串
jsonStr := "{\\"name\\":\\"Go 语言基础入门\\",\\"author\\":\\"fxtack\\",\\"publishTime\\":" +
"\\"2021-07-03T19:00:04.1716455+08:00\\"}"
// 声明一个空的 book 来接受结果
var bookObj Book
// 注意!反序列化函数 json.Unmarshal 的参数一个是 JSON 字符串的字节切片
// 一个是用于接受反序列化结果的对象引用。或者说传入的对象必须是可寻址的
json.Unmarshal([]byte(jsonStr), &bookObj)
fmt.Printf("书名为 《%s》, 作者是 %s, 发布时间为 %s",bookObj.Name, bookObj.Author, bookObj.PublishTime)
}
运行结果如下。
书名为 《Go 语言基础入门》, 作者是 fxtack, 发布时间为 2021-07-03 19:00:04.1716455 +0800 CST
结果输出是通过 bookObj 对象引用其属性来完成的,其字段不是空值,可见序列化完成。
注意
- 如果存在无法匹配到结构体字段的 JSON 字段,Go 反序列化时会无视这些字段。
- 如果结构体字段在 JSON 中没有字段与之匹配,Go 反序列化时也会无视这些字段,其值为结构体字段类型的默认值。
2. 反序列化完整代码
接下来考虑完整的过程,将上文中序列化的 .json
文件进行读取,实现反序列化,其流程如下:
代码如下:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"time"
)
type Book struct {
Name string `json:"name"`
Author string `json:"author"`
PublishTime time.Time GolangGo 语言 JSON 的序列化与反序列化实践