Eserde:不止步于第一个反序列化错误
Eserde 是一个专门解决 Rust 中反序列化错误报告问题的库。与传统的 serde 不同,它能够一次性报告多个错误,而不是在遇到第一个错误时就终止。这极大地改善了开发者体验,减少了调试和修正的时间。
什么是问题?
Rust 的 serde 库是目前最流行的(反)序列化工具。但它的设计有一个缺陷:当发生反序列化错误时,serde 会立即停止并只返回第一个错误信息。这对用户提交的数据处理(如 REST API 请求体)来说是一个大问题。例如,如果用户提交了一个包含多处错误的 JSON 数据,API 只能一次反馈一个错误,迫使用户进入慢且令人沮丧的反馈循环:
- 发送请求。
- 收到一个错误。
- 修复错误。
- 回到第 1 步,直到所有错误都被修复。
这种方式对开发者来说并不友好。我们应该可以同时报告多个错误,从而减少 API 交互次数,提高效率。
案例分析:无效的 JSON 数据
考虑以下结构作为参考示例:
#[derive(Debug, serde::Deserialize)]
struct Package {
    version: Version,
    source: String,
}
#[derive(Debug, eserde::Deserialize)]
struct Version {
    major: u32,
    minor: u32,
    patch: u32,
}
我们尝试通过 serde_json 对一个无效的 JSON 数据进行反序列化:
{
    "version": {
        "major": 1,
        "minor": "2"
    },
    "source": null
}
代码如下:
let payload = r#"
{
    "version": {
        "major": 1,
        "minor": "2"
    },
    "source": null
}"#;
let error = serde_json::from_str::<Package>(&payload).unwrap_err();
assert_eq!(
    error.to_string(),
    r#"invalid type: string "2", expected u32 at line 5 column 24"#
);
正如预期,serde_json 只返回了第一个错误:“字段 version.minor 类型错误”。
但是,这段 JSON 实际上还有其他问题:
- version结构缺少- patch字段。
- source字段不能为- null。
切换到 eserde 后,我们可以一次性捕获所有这些错误:
#[derive(Debug, eserde::Deserialize)]
struct Package {
    version: Version,
    source: String,
}
#[derive(Debug, eserde::Deserialize)]
struct Version {
    major: u32,
    minor: u32,
    patch: u32,
}
let payload = r#"
{
    "version": {
        "major": 1,
        "minor": "2"
    },
    "source": null
}"#;
let errors = eserde::json::from_str::<Package>(&payload).unwrap_err();
assert_eq!(
    errors.to_string(),
    r#"Something went wrong during deserialization:
- version.minor: invalid type: string "2", expected u32 at line 5 column 24
- version: missing field `patch`
- source: invalid type: null, expected a string at line 7 column 22
"#
);
现在,我们可以在一次反馈中告诉用户需要修复的三个问题。
如何使用 eserde?
要在项目中使用 eserde,只需将以下依赖项添加到你的 Cargo.toml 文件中:
[dependencies]
eserde = { version = "0.1" }
serde = "1"
接下来,你需要:
- 将所有的 #[derive(serde::Deserialize)]替换为#[derive(eserde::Deserialize)]。
- 切换到基于 eserde的反序列化函数。
JSON 支持
eserde 提供了对 JSON 的原生支持,启用方法是在 Cargo.toml 中激活 json 特性:
[dependencies]
eserde = { version = "0.1", features = ["json"] }
serde = "1"
如果你正在处理 JSON 数据:
- 替换 serde_json::from_str为eserde::json::from_str。
- 替换 serde_json::from_slice为eserde::json::from_slice。
注意,eserde::json 不支持从读取器反序列化(即没有 serde_json::from_reader 的等价功能)。
此外,eserde 还提供了与 axum 的集成模块 eserde_axum,可以用作 axum 内置 JSON 解析器的替代方案。
兼容性
eserde 设计上最大程度地与 serde 兼容。derive(eserde::Deserialize) 会同时实
