使用Serde进行反序列化时,是否有一种方法可以允许未知的枚举标签?

9
我正在对一个带有标签的枚举进行反序列化:
#[derive(Deserialize)]
enum Foo {
    A(A),
    B(B),
    C(C),
}

如果 Serde 遇到不是 ABC 的标签,它会抛出一个错误。是否有任何方法可以为未知的标签添加一个通用变体?如果它只记录标签,我会很高兴:

#[derive(Deserialize)]
enum Foo {
    A(A),
    B(B),
    C(C),
    #[serde(unknown_tag)]
    Unknown(String),
}

2
#[serde(other)],但它不会记录标签。 - Sven Marnach
啊,是的,不确定我怎么错过了那个,谢谢! - Timmmm
1个回答

7
你可以使用未标记的枚举完成这个任务。具体细节取决于你想要做什么。思路是将Foo包装成MaybeFoo,其中MaybeFoo有一个“通用”的类型作为第二选择进行反序列化。
在下面的示例中,我们使用serde_json::Value作为虚拟类型,因为它的Deserialize实现是通用的,可以反序列化任何有效的JSON。如果你的源格式不同,你可能需要一个不同的反序列化器或者自己实现Deserialize
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
enum Foo {
  A(u64),
  B(f32),
  C(String),
}

// MaybeFoo is untagged, which also means it "looks" exactly
// like a Foo when serialized/deserialized. 
#[derive(serde::Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum MaybeFoo {
    Foo(Foo),
    Other(serde_json::Value)
}
< p > MaybeFoo 是一个“无标签”的枚举类型,Serde 会尝试将其反序列化为 Foo,如果失败则尝试将其反序列化为 serde_json::Value,后者始终会成功(如果来源于 JSON)。< /p >
fn main() {
    // Lets create a Foo and serialize it
    let foo = Foo::B(0.0);
    let foo_json = serde_json::to_string(&foo).unwrap();
    println!("{}", &foo_json);

    // Deserialize works as expected
    let foo_json = "{\"B\":0.0}";
    assert!(serde_json::from_str::<Foo>(&foo_json).unwrap() == foo);

    // Deserializing as a `MaybeFoo` works as expected
    assert!(serde_json::from_str::<MaybeFoo>(&foo_json).unwrap() == MaybeFoo::Foo(foo));    

    // Deserializing something else is not a `Foo`!
    let foo_json = "{\"Unknown\":0.0}";
    let foo = serde_json::from_str::<MaybeFoo>(&foo_json).unwrap();

    // Prints "Other(Object({"Unknown": Number(0.0)}))"
    println!("{:?}", &foo);
}

你可以使用serde_json的API来检查未知的变体,如果它看起来像一个映射,那么可以提取标签。如果这是你唯一的兴趣,第二个MaybeFoo的变体也可以是HashMap<String,serde :: de :: IgnoredAny>,它将反序列化任何映射,将标记记录为String并丢弃值。但是,这假定未知值是带标签的值。

谢谢!如果我不使用JSON,有没有一种serde :: Value类型?我似乎找不到。 - Timmmm
1
这取决于底层格式,需要有人对“其他”情况进行反序列化,而没有所谓的“通用”反序列化器。如果底层格式是基于字符串的,则HashMap<String, String>(或者像上面那样,HashMap<String, serde::de::IgnoredAny>)可能有效。 - user2722968

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接