使用 Serde 反序列化时,有没有办法允许未知的枚举标签?

Tim*_*mmm 5 enums rust serde

我正在反序列化一个标记的枚举:

#[derive(Deserialize)]
enum Foo {
    A(A),
    B(B),
    C(C),
}
Run Code Online (Sandbox Code Playgroud)

如果 Serde 遇到一个不是A,B或的标签C,那么它会抛出一个错误。有什么方法可以为未知标签添加一个包罗万象的变体?如果它只记录标签,我会很高兴:

#[derive(Deserialize)]
enum Foo {
    A(A),
    B(B),
    C(C),
}
Run Code Online (Sandbox Code Playgroud)

use*_*968 7

您可以为此使用未标记的枚举。详细信息取决于您具体想要做什么。这个想法是包装Foo成一个MaybeFoo,其中MaybeFoo有一个“通用”类型可以反序列化为第二选择。

在下面的示例中,我们使用 aserde_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)
}
Run Code Online (Sandbox Code Playgroud)

MaybeFoo是一个“未标记”的枚举,Serde 将尝试反序列MaybeFoo化为 a Foo- 如果失败 - 因为serde_json::Value它总是会成功(如果源自 JSON)。

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);
}
Run Code Online (Sandbox Code Playgroud)

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