浅谈 Serde

强大的序列化/反序列化框架

技术
技术Rust序列化

2026-03-23

序列化与反序列化在某些编程语言中一直是让人头疼的问题。现在得益于 Rust 语言优秀的设计,社区诞生了 Serde 这一大杀器,让序列化与反序列化变得前所未有的统一、简单与优雅。

何为(反)序列化

一句话概括,序列化(serialization)就是将内存中的数据结构转换成可以存储或传输的字节流或字符串的过程。

反序列化(deserialization)就是倒过来,将字节流或字符串还原成内存中的数据结构。

Serde 数据模型

在代码层面,Serde 数据模型的序列化部分由 Serializer 特征定义,反序列化部分则由 Deserializer 特征定义。它们提供了一种将每个 Rust 数据结构映射到 29 种可能类型之一的方式。Serializer 特征的每个方法都对应数据模型中的一种类型。

当从某种格式反序列化一个数据结构时,数据结构的 Deserialize 实现负责通过向 Deserializer 传递一个 Visitor 实现来将数据结构映射到 Serde 数据模型,该 Visitor 实现能够接收数据模型的各种类型;而数据格式的 Deserializer 实现负责通过调用且仅调用一个 Visitor 方法,将输入数据映射到 Serde 数据模型。

Serde 数据模型是 Rust 类型系统的简化形式。它包含以下 29 种类型:

映射到数据模型

对于大多数 Rust 类型,它们到 Serde 数据模型的映射是直接的。例如,Rust 的 bool 类型对应于 Serde 的 bool 类型。Rust 的元组结构体 Rgb(u8, u8, u8) 对应于 Serde 的元组结构体类型。

但这些映射并不一定要是直接的。SerializeDeserialize 特征可以在 Rust 类型和 Serde 数据模型之间执行任何适合用例的映射。

以 Rust 的 std::ffi::OsString 类型为例。此类型表示平台原生的字符串。在 Unix 系统上,它们是任意的非零字节;在 Windows 系统上,它们是任意的非零 16 位值。将 OsString 映射到 Serde 数据模型,以下类型似乎是自然的选择:

相反,OsStringSerializeDeserialize 实现通过将 OsString 视为 Serde 枚举来映射到 Serde 数据模型。实际上,它的行为就好像 OsString 被定义为以下类型一样,尽管这与它在任何单个平台上的定义都不匹配。

enum OsString {
    Unix(Vec<u8>),
    Windows(Vec<u16>),
    // 以及其他平台
}

映射到 Serde 数据模型时的这种灵活性是深刻而强大的。在实现 SerializeDeserialize 时,要注意你的类型所处的更广泛上下文,最直观的映射可能并非最佳选择。

序列化为 JSON

社区提供了一系列基于 Serde 或兼容 Serde 的库,包括 serde_jsonserde_yamltoml 等。它们为开发者提供了多种序列化方案。

说明
serde_json处理 JSON 格式
serde_yaml处理 YAML 格式,现已停止维护
serde_saphyr处理 YAML 格式
toml处理 TOML 格式
serde-xml-rs处理 XML 格式
ciborium处理 CBOR 格式,现已停止维护

这里我们着重介绍 serde_json,它与 Serde 深度绑定,提供了一系列用于将 Serde 数据序列化为 JSON 格式或将 JSON 反序列化为 Serde 数据的方法。

let data = r#"
{
    "name": "John Doe",
    "age": 43,
    "phones": [
        "+44 1234567",
        "+44 2345678"
    ]
}"#;

// 把符合 JSON 格式的字符串解析为 `serde_json::Value`,该类型能被轻易地序列化或反序列化
let v: Value = serde_json::from_str(data).unwrap();

// 像访问 JSON 那样访问 `serde_json::Value`
println!("Please call {} at the number {}", v["name"], v["phones"][0]);

另外它还提供一个 json 宏,其允许你“所见即所得”地直接创建 serde_json::Value

let code = 200;
let features = vec!["serde", "json"];

let value = json!({
    "code": code, // 变量或常量
    "success": code == 200, // 表达式
    "payload": {
        features[0]: features[1] // 数组元素
    }
});

需要注意的是,json 宏要求其中的任何值都必须实现了 Serialize 特征。

使用派生

派生宏基础用法

Serde 提供了派生宏,用于为你定义的数据结构快速生成 SerializeDeserialize 特征的实现。就像你用于自动派生内置 CloneCopyDebug 或其他特征的实现一样。

它能够为大多数结构体和枚举生成实现,包括那些带有复杂泛型类型或特征约束的类型。在极少数情况下,对于特别复杂的类型,你可能需要手动实现这些特征。

记得在 Cargo.toml 中为 serde 添加 features = ["derive"]。然后你就可以畅快地为数据结构派生特征了:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };

    let serialized = serde_json::to_string(&point).unwrap();
    println!("serialized = {}", serialized);
    // 输出 serialized = {"x":1,"y":2}
    // 表明序列化成功
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    // 输出 deserialized = Point { x: 1, y: 2 }
    // 表明反序列化成功
}

在后续介绍其他派生宏时,默认都使用 serde_json 的序列化结果来演示。

使用宏来指定属性

如果只使用派生宏来实现 SerializeDeserialize 特征,灵活性并不高。所幸 Serde 提供了一系列用于指定属性的派生宏,从而大幅增强了派生的灵活程度。

下面介绍一些常用的派生宏:

手动实现序列化

Serde 所提供的派生宏功能已经足够强大,以至于大多数情况下我们完全不需要手动去实现 SerializeDeserialize。但在某些极端场景下,可能需要手动对序列化进行高度定制。

首先来看看这两个特征:

pub trait Serialize {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer;
}
pub trait Deserialize<'de>: Sized {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>;
}

我们看到,在特征中又出现了 SerializerDeserializer 两个新特征。它们和 SerializeDeserialize 的关系是什么?

实际上,SerializeDeserialize 定义数据结构的序列化/反序列化逻辑。由需要被序列化的类型实现。而 SerializerDeserializer则是定义了序列化和反序列化的具体格式(例如 JSON、YAML 等),一般由序列化库(如 serde_jsonserde_yaml 等)实现。

具体来说,对于一个完整的序列化过程(从数据结构到字符串),会发生:

  1. 用户通过库提供的函数(如 serde_json::to_string)来开始序列化过程;
  2. 库提供的函数内部调用类型实现的 serialize 方法;
  3. serialize 方法接收库提供的实现 Serializer 的类型;
  4. 实现 Serializer 的类型实例会提供多种方法(例如 serialize_i32serialize_tupleserialize_struct 等)来将数据结构进行具体的序列化;
  5. 多种方法将序列化产生的字符串输出到缓冲区;
  6. 缓冲区的字符串被层层传回,最后由库提供的函数返回。

对于 Serde 生态中成熟的序列化库来说,我们并不需要实现 SerializerDeserializer,只需要对类型实现 SerializeDeserialize。但是我们需要熟悉 SerializerDeserializer 提供的一些方法。例如:

参考资料