rust - Serde 结构版本检查

标签 rust serde

我想添加一个简单的版本方案 + 检查我的结构:

#[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)
}

Playground

关于rust - Serde 结构版本检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70366168/

相关文章:

Rust 等同于 Swift 对协议(protocol)的扩展方法?

rust - 分割字符串并返回Vec <String>

rust - 如何在不冒犯借用检查器的情况下进行计算状态转换?

unit-testing - 比较 Rust 中的相等性函数

serialization - 如何将 BSON 反序列化为通用对象?

rust - 如何概括使用不同类型的键传递给函数的HashMap?

json - 如何使用 Serde 反序列化包含空值的 JSON 文件?

rust - 特征 `serde::Deserialize<' _ >` is not implemented for ` diesel_geography::types::GeogPoint`

rust - 如何使用 serde 序列化选项?

struct - 如何在强制使用 "new"构造函数的同时使结构的所有字段公开可读