我想添加一个简单的版本方案 + 检查我的结构:
#[derive(Serialize, Deserialize)]
struct Versioned {
version: u32,
other_field: String,
}
impl Versioned {
const FORMAT_VERSION: u32 = 42;
}
这个想法相当简单:version
字段被反序列化(如果不存在则为 0*),如果它不等于 FORMAT_VERSION
则抛出错误。然后,处理结构的其余部分。此处的顺序很重要:否则,反序列化错误可能不正确(您可能会得到类似“缺少字段 new_field
”而不是“版本太旧”的信息)。
我尝试了一些关于包装器类型和自定义反序列化器的想法,但它们都没有提供良好的用户体验。到目前为止我最好的成绩:
#[derive(Serialize, Deserialize)]
struct Payload {
some_field: String,
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "version")]
enum Versioned {
#[serde(rename = "0")]
Inner(Payload),
}
它使用枚举包装器进行版本检查,并使用两个 From
实现来轻松进行类型转换。缺点是:
- 该字段没有默认值*(=不向后兼容未版本化的数据)
- 旧版本的错误消息(“错误:未知变体
42
,预计0
在第 2 行第 17 列”而不是类似“无效版本”的内容)<
* 注意:向后兼容性要求使事情变得更加困难。所以请记住,伙计们,请始终立即为所有数据添加一个 version
字段!
最佳答案
您可以通过为 version
字段编写自定义反序列化函数来实现这两点。您可以通过在字段上放置 #[serde(default)]
使其成为可选的。
自定义反序列化函数可以包含您想要的任何逻辑来检查版本号并生成所需的错误消息。
#[derive(Serialize, Deserialize, Debug)]
struct Versioned {
#[serde(deserialize_with = "ensure_version", default)]
version: u32,
other_field: String,
}
impl Versioned {
const FORMAT_VERSION: u32 = 42;
}
fn ensure_version<'de, D: Deserializer<'de>>(d: D) -> Result<u32, D::Error> {
let version = u32::deserialize(d)?;
if version != Versioned::FORMAT_VERSION {
return Err(D::Error::custom("Version mismatch for Versioned"));
}
Ok(version)
}
关于rust - Serde 结构版本检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70366168/