Golang 内置的 encoding/gob 实现了序列化 / 反序列化一个 golang 结构的能力。
电脑上如果 PDF 不展示或者展示不正常,使用 Chrome 并安装 PDF Viewer 插件。其他情况请下载文件查看:golang-package-gob.pdf。
Rob Pike 描述了 gob 的 设计理念:
- 易用:Go 有反射能力,schema 打包在 buffer 中,decoder 不需要知道 struct 的结构即可解包;因此也不需要一个专门的 "protocol compiler"(比如 Protobuf 的 protoc)
- Go-centric:因为上面的原因, gob 并不打算实现成各语言通用的
- 性能:gob 使用二进制协议(而不是 JSON / XML 这种文本协议)并尽量使编解码高效
同时描述了 gob 与 Protobuf 理念上的不同:
- PB 只支持 struct,不能只 encode 一个整数或者一个数组
- PB 对 field 的 required / optional 设计比较啰嗦(Rob Pike 写这篇文章时只有 PB2,PB3 部分解决了它说的问题),而 gob 并不要求严格的字段对应,一切 field 都是 optional
interface value 的处理
gob.Encoder
可以 encode 一个 interface value,但是你需要提前将其具体类型注册;如果不注册具体类型,而又可能有多个类实现同个 interface,那 gob 在解包时并无法知道要将该 interface value 解为什么具体类型。打包好的二进制会包含类型名。比如:
package main
import (
"bytes"
"encoding/gob"
"log"
"math"
)
// Point 实现了 Pythagoras interface
type Point struct {
X, Y int
}
func (p Point) Hypotenuse() float64 {
return math.Hypot(float64(p.X), float64(p.Y))
}
type Pythagoras interface {
Hypotenuse() float64
}
func main() {
var network bytes.Buffer
// 注册具体的类型。无论是 encode 还是 decode 的代码都需要注册。
gob.Register(Point{})
enc := gob.NewEncoder(&network)
var p1 Pythagoras = Point{3, 4}
// 这里需要传 &p1 而不是 p1。如果传的是 p1,gob 打包的是具体的 Point 类型,
// 而不是 Pythagoras interface 类型。
// 原因见:https://blog.golang.org/laws-of-reflection(未理解)
if err := enc.Encode(&p1); err != nil {
log.Fatal("encode:", err)
}
dec := gob.NewDecoder(&network)
var p2 Pythagoras
if err := dec.Decode(&p2); err != nil {
log.Fatal("decode:", err)
}
// 打印 {3, 4}
fmt.Println(p2)
}
扩展性
任何类型实现了下面的接口后,可以实现自定义的二进制格式:
- Encode:
GobEncoder
orencoding.BinaryMarshaler
- Decode:
GobDecoder
orencoding.BinaryUnmarshaler