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.
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)
}
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);
}
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.