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

时间:2020-08-24 11:56:48

标签: enums rust serde

我正在反序列化带标签的枚举:

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

1 个答案:

答案 0 :(得分:3)

您可以为此使用untagged enum。详细信息取决于您要确切执行的操作。这个想法是将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)
}

MaybeFoo是一个“未标记的”枚举,Serde将尝试将MaybeFoo反序列化为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);
}

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

相关问题