使用 Serde 反序列化时,有没有办法允许未知的枚举标签? [英] Is there a way to allow an unknown enum tag when deserializing with Serde?

查看:63
本文介绍了使用 Serde 反序列化时,有没有办法允许未知的枚举标签?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm deserializing a tagged enum:

#[derive(Deserialize)]
enum Foo {
    A(A),
    B(B),
    C(C),
}

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

If Serde encounters a tag that isn't A, B or C, then it will throw an error. Is there any way to add a catch-all variant for unknown tags? I'd be happy if it only records the tag:

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

推荐答案

您可以使用 untagged枚举为此.细节取决于你想要做什么.这个想法是将 Foo 包装成一个 MaybeFoo,其中 MaybeFoo 有一个通用"反序列化的类型作为第二选择.

You can use an untagged enum for this. The details depend on what you want to do exactly. The idea is to wrap Foo into a MaybeFoo, where MaybeFoo has a "universal" type to deserialize into as the second choice.

在下面的示例中,我们使用 serde_json::Value 作为虚拟类型,因为它的 Deserialize 实现是通用的,可以反序列化任何有效的 JSON.如果您的源格式不同,您可能需要不同的反序列化器或自己实现Deserialize.

In the example below, we use a serde_json::Value as a dummy-type, as its implementation of Deserialize is universal as can deserialize anything that is valid JSON. If your source format is different, you may need a different deserializer or implement Deserialize yourself.

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

The MaybeFoo is an "untagged" enum and Serde will try to deserialize MaybeFoo as a Foo and - if that fails - as serde_json::Value which will always succeed (if sourced from 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 并丢弃该值.但是,这假定未知值是标记值.

You can use serde_json's API to inspect the unknown variant and - if it looks like a map - extract the tag. If this is your only interest, the second variant of MaybeFoo could also be a HashMap<String, serde::de::IgnoredAny>, which will deserialize any map, record the tag as a String and throw away the value. This presumes, however, that the unknown value is a tagged value.

这篇关于使用 Serde 反序列化时,有没有办法允许未知的枚举标签?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆