背景
在软件开发过程中序列化和反序列化是一个重要的步骤,它能够让数据在存储和传输变得更简单,高效。在 Go 中有很多现成的包让序列化和反序列化的工作的变的很简单。本文主要对 JSON, XML, Gob, Protocol Buffers (protobuf), MessagePack, and YAML.等流行格式的学习和记录。
序列化和反序列化
序列化是将集合类型的数据结构转换为可以方便保存到文件或通过网络传输的格式的过程。反序列化是相反的过程,就是将序列化的数据反过来序列化成初始的数据集合。常见的序列化格式包括 JSON、XML 和二进制格式,例如 Gob、Protocol Buffers、MessagePack 和 YAML。
每种格式都有其使用的场景和优势:
- JSON:适用于服务间的数据交换;作为可读且易编辑的配置文件和数据文件;用于 API 响应和请求体。结构直观简洁,相较于XML更轻量。
- XML:适用于一些严格要求文档结构的场景,如SVG、RSS;C# 中作为配置文件的格式,SOAP协议中使用。可扩展自定义标签,支持严格的模式定义和验证
- Gob:Go 语言标准库一部分,用于go应用之间的高效数据序列化和反序列化,或者RPC 通信,传输数据性能更高,占用空间小,使用方便。
- Protocol Buffers:适用于对性能和数据一致性要求高的系统,如微服务之间数据传输,尤其适用于大型系统。高性能且有明确的消息结构
- MessagePack :适用于带宽和性能要求高的移动应用或游戏网络通信
- YAML :广泛应用于配置文件
使用何种格式主要依赖于你的场景和要求,比如可读性,性能或兼容性的要求。
序列化和反序列化的实践
JSON
Json是一种轻量级数据交换格式,简单易懂编写放便,也方便机器解析和生成。在 Go 中,encoding/json 包提供了使用 JSON 的方法。json.Marshal 用于序列化数据 json.Unmarshal 方法用于反序列数据
go
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty" `
}
// 序列化
data, err := json.Marshal(p)
if err != nil {
fmt.Println("序列化失败:", err)
return
}
// 反序列化
var p2 Person
err = json.Unmarshal(data, &p2)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
例子中使用 struct 的标签对Json 字段名称的自定义, 标签中 omitempty 的标识作用是省略序列化输出的空字段。始终对序列化和反序列化过程进行错误处理,避免数据损坏或崩溃。
XML
XML(可扩展标记语言)是另一种用于序列化的格式,特别是在需要严格数据模式的应用程序中。在 Go 中,encoding/xml
包提供了使用 JSON 的方法。xml.MarshalIndent
用于序列化,并带有缩进以提高可读性, xml.Unmarshal
用于反序列化。
go
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}
// 序列化
data, err := xml.MarshalIndent(p, "", " ")
if err != nil {
fmt.Println("序列化失败:", err)
return
}
var p2 Person
err = xml.Unmarshal(data, &p2)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
例子中 struct 的标签也支持XML字段的定义,和json 不同是结构体中多了使用 XMLName 管理 xml 的 namespace。对于错误的处理也是始终都要进行处理的。
Gob
Gob 格式是golang 专用的二进制序列化的序列化格式,简洁且高效。使用 encoding/gob
包可以完成对应序列化和反序列化的工作。
go
type Person struct {
Name string
Age int
Email string
}
// 序列化
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(p)
if err != nil {
fmt.Println("Error serializing:", err)
return
}
fmt.Println("Serialized Gob:", buffer.Bytes())
// 反序列化
var p2 Person
decoder := gob.NewDecoder(&buffer)
err = decoder.Decode(&p2)
if err != nil {
fmt.Println("Error deserializing:", err)
return
}
使用 Gob 序列化需要先使用NewEncoder 初始化一个序列化器在调用对应序列化方法 Encode和 Decode。Gob 相较于 JSON 和XML 更加轻量,处理更快速。在序列化和反序列过程保证类型安全。所以在处理一些复杂或接口类型时需要使用 gob.Register 进行注册,避免编码错误。
ProtocolBuffers(Protobuf)
protobuf 是谷歌开发的一种与语言和平台无关的机制,相较于 JSON 和XML 更加轻量且处理更快速。使用 protobuf 需要先定义一个数据类型文件 .proto 文件。然后再对该文件进行编译生成 Go 代码。进行序列化和反序列工作只需要引用 github.com/golang/protobuf/proto
包。
ini
// .proto 文件
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
string email = 3;
}
// go 文件
// 序列化
data, err := proto.Marshal(p)
if err != nil {
log.Fatal("序列化失败: ", err)
}
// 反序列化
var p2 personpb.Person
err = proto.Unmarshal(data, &p2)
if err != nil {
log.Fatal("反序列化失败: ", err)
}
使用 protoc-gen-go 等库可以轻松将protobuf集成在go项目中,且兼容性可以支持向后和向前兼容。因为可以在 message 中添加字段而不用去动现有的代码。
MessagePack
messagePack 也是一种高效的二进制序列化格式,特别适用在关注带宽的场景中。使用 github.com/vmihailenco/msgpack/v5
包即可完成序列化相关工作。
go
// 序列化
data, err := msgpack.Marshal(p)
if err != nil {
log.Fatal("序列化失败: ", err)
}
// 反序列
var p2 Person
err = msgpack.Unmarshal(data, &p2)
if err != nil {
log.Fatal("反序列化失败: ", err)
}
Yaml
通常应用于配置文件中,是一种方便阅读的序列化格式。使用 gopkg.in/yaml.v2
包即可完成序列化相关工作。
go
// 序列化
data, err := yaml.Marshal(p)
if err != nil {
log.Fatal("序列化失败: ", err)
}
// 反序列
var p2 Person
err = yaml.Unmarshal(data, &p2)
if err != nil {
log.Fatal("反序列化失败: ", err)
}
其作为配置文件时读写方便,但需要注意缩进的问题。YAML 还支持复杂数据结构,包括嵌套映射和列表。
yaml
//yaml 配置文件
server:
name: tool-web
port: 8080
redis:
dns: "..."
maxConn: 10