Go语言自定义序列化与反序列化核心是实现json.Marshaler/json.Unmarshaler等接口,精确控制字段编解码逻辑,适用于兼容旧协议、脱敏、嵌入元数据或适配非标格式;二进制场景可选用BinaryMarshaler/BinaryUnmarshaler提升性能,并需通过单元测试和魔数校验保障兼容性与健壮性。

Go语言中,自定义序列化与反序列化工具的核心在于绕过标准库(如encoding/json)的默认行为,精确控制字段如何转为字节流、又如何还原。这通常用于兼容旧协议、处理敏感字段、嵌入元数据、或适配非标准格式(如带校验头的二进制结构)。关键不是重造轮子,而是通过实现json.Marshaler/json.Unmarshaler接口,或使用BinaryMarshaler/BinaryUnmarshaler,让类型自主决定编解码逻辑。
用 Marshaler/Unmarshaler 接口定制 JSON 行为
当结构体字段不能直接映射为 JSON(比如需要加密、脱敏、拼接字符串、跳过零值等),可让类型实现这两个接口:
- 实现
MarshalJSON()方法:返回字节数组和错误;内部可手动构造map[string]interface{}再调用json.Marshal,也可用json.Encoder写入bytes.Buffer - 实现
UnmarshalJSON([]byte)方法:先解析原始字节(如用json.Unmarshal转为map[string]json.RawMessage),再按需赋值,支持字段名校验、默认值填充、类型转换容错 - 注意:若嵌套结构也需定制,需逐层实现;避免在
UnmarshalJSON中直接调用json.Unmarshal(&struct{}, data)导致无限递归
二进制格式自定义:BinaryMarshaler 更轻量高效
对于性能敏感或协议固定场景(如网络包、本地缓存),二进制序列化更合适。Go 的encoding/gob虽方便但不跨语言,此时推荐手写:
- 实现
MarshalBinary() ([]byte, error):用binary.Write将字段按顺序写入bytes.Buffer,支持大小端选择、长度前缀、CRC校验字节追加 - 实现
UnmarshalBinary([]byte) error:用binary.Read按相同顺序读取,提前检查字节长度是否足够,错误时返回明确提示(如io.ErrUnexpectedEOF) - 示例:时间字段可只存 int64 秒级时间戳,而非 RFC3339 字符串,节省空间且解析快
零拷贝与复用:避免频繁分配内存
高频序列化场景下,json.Marshal每次都会分配新切片。优化方式包括:
- 用
sync.Pool缓存*bytes.Buffer或预分配的[]byte,在MarshalJSON中Get()后Reset()复用 - 对
UnmarshalJSON,接收参数用json.RawMessage延迟解析,仅在真正访问字段时才解,减少中间对象生成 - 结构体字段尽量用基本类型或固定长度数组(如
[16]byte代替string),便于直接unsafe.Slice转换(需确保内存布局安全)
测试与兼容性保障
自定义编解码易出隐含 bug,必须覆盖边界情况:
- 写单元测试:正向用已知结构体序列化再反序列化,比对字段值;反向测试空指针、nil slice、NaN float、超长字符串等非法输入是否返回合理错误
- 版本演进时加字段,老数据反序列化应能忽略新字段(
json.RawMessage跳过未知键);删字段则需保留占位逻辑,防止 panic - 跨服务通信时,建议在序列化结果开头加 2 字节魔数(如
0xCAFE)和 1 字节版本号,反序列化前校验,快速识别协议不匹配
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











