JSON设计原理
Go 语言通过 encoding/json 对外提供标准的 JSON 序列化和反序列化方法,即 encoding/json.Marshal 和 encoding/json.Unmarshal,它们也是包中最常用的两个方法。
序列化和反序列化
序列化和反序列化的开销完全不同,JSON 反序列化的开销是序列化开销的好几倍,相信这背后的原因也非常好理解。Go 语言中的 JSON 序列化过程不需要被序列化的对象预先实现任何接口,它会通过反射获取结构体或者数组中的值并以树形的结构递归地进行编码,标准库也会根据 encoding/json.Unmarshal 中传入的值对 JSON 进行解码。
接口
JSON 标准库中提供了 encoding/json.Marshaler 和 encoding/json.Unmarshaler 两个接口分别可以影响 JSON 的序列化和反序列化结果:
type Marshaler interface {
MarshalJSON() ([]byte, error)}type Unmarshaler interface {
UnmarshalJSON([]byte) error}
除了这两个方法之外,Go 语言其实还提供了另外两个用于控制编解码结果的方法,即 encoding.TextMarshaler 和 encoding.TextUnmarshaler
type TextMarshaler interface {
MarshalText() (text []byte, err error)}type TextUnmarshaler interface {
UnmarshalText(text []byte) error}
标签
Go 语言的字段一般都是驼峰命名法,JSON 中下划线的命名方式相对比较常见,使用标签这一特性直接建立键与字段之间的映射关系
常见的两个标签是 string 和 omitempty,前者表示当前的整数或者浮点数是由 JSON 中的字符串表示的,而另一个字段 omitempty 会在字段为零值时,直接在生成的 JSON 中忽略对应的键值对,例如:“age”: 0、“author”: “” 等。标准库会使用如下所示的 encoding/json.parseTag 来解析标签:
JSON 标准库中的合法标签是什么形式的:标签名和标签选项都以 , 连接,最前面的字符串为标签名,后面的都是标签选项。
序列化
encoding/json.Marshal 是 JSON 标准库中提供的最简单的序列化函数,它会接收一个 interface{} 类型的值作为参数
它的实现:
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState()
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()…)
encodeStatePool.Put(e)
return buf, nil}
上述方法会调用 encoding/json.newEncodeState 从全局的编码状态池中获取 encoding/json.encodeState,随后的序列化过程都会使用这个编码状态,该结构体也会在编码结束后被重新放回池中以便重复利用。
反序列化
标准库会使用 encoding/json.Unmarshal 处理 JSON 的反序列化,与执行过程确定的序列化相比,反序列化的过程是逐渐探索的过程,所以会复杂很多,开销也会高出几倍。
在真正执行反序列化之前,我们会先调用 encoding/json.checkValid 验证传入 JSON 的合法性保证在反序列化的过程中不会遇到语法错误的问题,在通过合法性的验证之后,标准库会初始化数据并调用 encoding/json.decodeState.unmarshal 开始反序列化
JSON 本身就是一种树形的数据结构,无论是序列化还是反序列化,都会遵循自顶向下的编码和解码过程,使用递归的方式处理 JSON 对象。