您可以为此使用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 并丢弃该值。但是,这假定未知值是标记值。