跳转到内容

Serde 数据模型

Serde 数据模型是一组语义操作,而不是内存中的统一 Value。它既能表达 JSON 的动态结构,也能表达二进制格式关注的 tuple、newtype 与 enum variant。

先给答案:Serde 的数据模型是格式无关的中间契约

Section titled “先给答案:Serde 的数据模型是格式无关的中间契约”

Serialize 不直接写 JSON 字符串,而是向 serializer 描述“这是 struct、seq、map 还是 enum”;Deserialize 也不直接读取某一种格式,而是请求 deserializer 提供所需的数据访问。格式实现只需对接这套契约,就能复用大量类型代码。

因此某格式不支持 borrowed string、tuple 或 enum 标签时,失败原因可能是数据模型能力不匹配,而不是 derive 代码错误。读源码应先确认模型调用,再看格式如何实现这些访问接口。

scalar
bool / signed / unsigned / float / char / str / bytes
wrapper
option / unit / unit_struct / newtype_struct
compound
seq / tuple / tuple_struct
map / struct
enum variants: unit / newtype / tuple / struct

Serializer 的标量入口从 serde_core/src/ser/mod.rs:427 的 serialize_bool 延伸到 serialize_str(:720)和 serialize_bytes(:755)。Option 与 unit 位于 :788-916;sequence、tuple、map、struct 分别从 :1006、:1062、:1188、:1220 开始。

Deserializer 侧对应 typed methods 位于 serde_core/src/de/mod.rs:959-1188:deserialize_bool、deserialize_str、deserialize_option、deserialize_seq、deserialize_map、deserialize_struct、deserialize_enum 等。

为什么区分 tuple、seq、struct、map

Section titled “为什么区分 tuple、seq、struct、map”

表面上它们都可表示为序列或键值对,但区别提供:

  • 已知长度和字段名,便于二进制布局;
  • 更精确的错误信息;
  • enum representation 与 schema 生成;
  • 格式可以对 struct 做专门优化。

若把一切压成 list/map,格式会失去静态语义,只能重新猜测。

自描述格式(如 JSON)可由下一个 token 决定类型,适合 deserialize_any(de/mod.rs:959)。非自描述二进制格式通常必须由目标类型调用 deserialize_u32、deserialize_tuple 等方法告诉格式“接下来期待什么”。

为什么 deserialize_any 不是万能接口

Section titled “为什么 deserialize_any 不是万能接口”

替代方案:所有格式只实现一个动态解析入口。

为什么不行:无 schema 二进制输入可能只有裸字节,无法知道 0x01 是 bool、u8 还是 enum tag。

证据:Deserializer 明确要求一整组 typed methods(de/mod.rs:945-1188),并把 deserialize_any 作为格式能力而非唯一协议。

Serde derive 支持外部标记、内部标记、相邻标记与无标记。属性模型的 TagType 定义在 serde_derive/src/internals/attr.rs:178-188。外部标记最流式;内部/相邻/untagged 在部分情况下需要先缓冲 Content 再二次反序列化。

场景 现象 原因 规避
二进制格式使用 untagged enum 大量回溯或不支持 输入缺少判别信息 使用显式 tag
bytes 当 seq 体积/速度下降 格式无法走 bytes 快路径 使用 serialize_bytes/bytes helper
字段顺序当协议保证 跨格式不一致 map/struct 顺序语义不同 明确 schema,不依赖 map 顺序
新旧版本 enum 变体不兼容 反序列化失败 tag/schema 演进未设计 unknown variant 策略与版本字段

中间协议应保留足够语义,不能为了接口少而把所有结构压平。语义越精确,格式实现越能选择零分配、定长和专用编码路径。

面试锚点

  • Serde 数据模型为什么比 JSON Value 丰富?
  • 自描述格式与非自描述格式有何差别?
  • untagged enum 为什么可能更贵?