序列化(Serialization)是把程序内存中的数据结构或对象转换成可存储、可传输的连续表示;反序列化则把这种表示重新解析回数据结构。
核心问题
内存对象依赖进程、语言运行时、地址空间和类型系统,不能直接写入文件、发到网络或交给另一个语言环境使用。序列化解决的是“如何把运行时对象变成稳定外部表示”的问题。
这个外部表示可以是文本,如 JSON、YAML;也可以是二进制,如 Protocol Buffers、MessagePack、CBOR 或自定义 Wire Format。
核心机制
一次序列化通常包含这些步骤:
- 选择 schema 或约定:决定字段名、字段编号、类型、默认值和兼容性规则。
- 遍历数据结构:把对象图、数组、字典和嵌套结构按约定展开。
- 编码基本类型:把整数、浮点数、字符串、时间、枚举和二进制块转换成目标表示。
- 标记边界:通过长度、分隔符、容器结构或字段 tag 让接收方知道每段数据在哪里结束。
- 处理缺省和未知字段:为版本升级保留兼容空间。
反序列化的方向相反:读取字节或文本,识别边界和字段,校验类型与约束,再重建目标语言中的对象。
工程用途
- 网络通信:Wire Protocol 通常依赖序列化把请求、响应和事件变成链路上的字节。
- 持久化:配置、缓存、快照、日志和离线队列都需要稳定的存储表示。
- 跨语言互操作:schema 明确的格式可以让 Java、Go、Python、Rust 等语言共享数据。
- 消息队列与事件流:事件需要被序列化后才能进入 Kafka、Redis Stream、文件日志或对象存储。
- 测试与回放:固定输入可以保存成测试向量,用来验证解析器和兼容性。
边界与常见坑
- 序列化不是压缩:序列化决定数据如何表达;压缩只是减少字节数,通常发生在序列化之后。
- 序列化不是加密:二进制不可读不等于安全,敏感数据仍要加密和鉴权。
- 不要盲目反序列化不可信输入:许多语言的对象反序列化机制可能触发代码执行、资源耗尽或类型混淆。
- 对象图不一定能直接序列化:循环引用、共享引用、文件句柄、线程、连接和指针需要特殊处理或禁止编码。
- 时间和数字要规范化:时区、精度、NaN、Infinity、大整数和浮点误差常导致跨语言差异。
- 版本演进要向前兼容:删除字段、重用字段编号、改变类型或改变默认值都可能破坏旧数据。