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)
会同时实