【问题标题】:How to only allow one field or the other with Serde?Serde 如何只允许一个字段或另一个字段?
【发布时间】:2021-11-04 03:10:03
【问题描述】:

假设我有这个结构:

use serde::{Serialize, Deserialize};

#[derive(Deserialize)]
struct MyStruct {
    field_1: Option<usize>, // should only have field_1 or field_2
    field_2: Option<usize>, // should only have field_1 or field_2
    other_field: String,
}

我怎样才能反序列化这个但只允许这些字段之一存在?

【问题讨论】:

  • 使用枚举,我猜。
  • 这两个字段是互斥的吗?或者,是否存在两者都存在的情况?
  • @Joe_Jingyu 它们应该是互斥的,我想阻止可能试图同时包含它们的情况。
  • 如果是这样,正如@Dev611 还建议的那样,使用enum 而不是struct 看起来不错。
  • 我应该包含在示例中,但struct 中还会有其他字段

标签: rust serde


【解决方案1】:

cmets 中使用枚举的建议可能是您最好的选择。您不需要用枚举替换您的结构,而是添加一个单独的枚举类型来表示此约束,例如:

use serde::{Serialize, Deserialize};

#[derive(Deserialize)]
enum OneOf {
    F1(usize), F2(usize)
}

#[derive(Deserialize)]
struct MyStruct {
    one_of_field: OneOf,
    other_field: String,
}

现在MyStructone_of_field 可以使用F1F2 进行初始化。

【讨论】:

    【解决方案2】:

    您可以反序列化您的结构,然后验证您的类型应该支持的所有不变量。您可以为此类型实现Deserialize,同时还可以依靠派生宏来完成繁重的工作。

    use serde::{Deserialize, Deserializer};
    
    #[derive(Debug, Deserialize)]
    #[serde(remote = "Self")]
    struct MyStruct {
        field_1: Option<usize>, // should only have field_1 or field_2
        field_2: Option<usize>, // should only have field_1 or field_2
        other_field: String,
    }
    
    impl<'de> Deserialize<'de> for MyStruct {
        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
            use serde::de::Error;
            
            let s = Self::deserialize(deserializer)?;
            if s.field_1.is_some() && s.field_2.is_some() {
                return Err(D::Error::custom("should only have field_1 or field_2"));
            }
            
            Ok(s)
        }
    }
    
    fn main() -> () {
        dbg!(serde_json::from_value::<MyStruct>(serde_json::json!({
            "field_1": 123,
            "other_field": "abc"
        })));
        dbg!(serde_json::from_value::<MyStruct>(serde_json::json!({
            "field_2": 456,
            "other_field": "abc"
        })));
        dbg!(serde_json::from_value::<MyStruct>(serde_json::json!({
            "field_1": 123,
            "field_2": 456,
            "other_field": "abc"
        })));
    }
    

    Playground

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-04
      • 2021-12-14
      • 2020-02-13
      • 2014-02-24
      • 2017-01-22
      • 2021-07-08
      • 2017-09-11
      • 2014-07-24
      相关资源
      最近更新 更多