I have an Enum like the one below
#[serde(rename_all="snake_case")]
pub enum Flag {
One,
Two,
Three { value: Option<Decimal>}
}
I want to match the following json representations to it
{"flag":"one"} // Working fine
{"flag":"two"} // Working fine
{"flag":{"three":{"value":###}} // Working fine -> Three { value: Some(###) }
{"flag":"three"} // not working, desired result -> Three { value: None }
How can I get such behavior?
I already tried:
#[serde(default)]
on the variant, but that will only match {"flag":{"three":{}}}
#[serde(untagged)]
on the variant, but this is applied to the full enumThreeNone
with the #[serde(rename="Three")]
attributePlease help
Assuming you want the Flag
as is, without any changes to the enum
and its variants. Then it becomes a bit tricky, due to the structure of your JSON.
When I encounter these simple vs complex forms of JSON data, then I usually implement it as multiple types. With an overall serde(untagged)
enum
.
Note: That the order of the variants matter in serde(untagged)
, as serde will try the variants sequentially, and stop when one successfully deserializes.
To keep your Flag
as is, then you can use serde(from = ...)
or serde(try_from ...)
to deserialize through the other type. The other types can be kept private as well.
For demonstration purposes, I've changed your Decimal
to u32
. But as long as Decimal
implements Deserialize
, then they are interchangeable.
It would look something like this:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(from = "ComplexFlag")]
pub enum Flag {
One,
Two,
Three { value: Option<u32> },
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum ComplexFlag {
Three(ThreeFlag),
Simple(SimpleFlag),
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
enum SimpleFlag {
One,
Two,
Three,
}
#[derive(Deserialize, Debug)]
struct ThreeFlag {
three: ThreeData,
}
#[derive(Deserialize, Debug)]
struct ThreeData {
value: Option<u32>,
}
Then all we need is the From<ComplexFlag> for Flag
:
impl From<ComplexFlag> for Flag {
fn from(flag: ComplexFlag) -> Self {
match flag {
ComplexFlag::Simple(flag) => match flag {
SimpleFlag::One => Self::One,
SimpleFlag::Two => Self::Two,
SimpleFlag::Three => Self::Three { value: None },
},
ComplexFlag::Three(flag) => Self::Three {
value: flag.three.value,
},
}
}
}
Testing it with the following:
#[derive(Deserialize, Debug)]
struct Data {
flag: Flag,
}
let json = [
r#"{"flag":"one"}"#,
r#"{"flag":"two"}"#,
r#"{"flag":"three"}"#,
r#"{"flag":{"three":{"value":123}}}"#,
];
for json in json {
let data = serde_json::from_str::<Data>(json);
println!("{:?}", data);
}
Then correctly yields:
Ok(Data { flag: One })
Ok(Data { flag: Two })
Ok(Data { flag: Three { value: None } })
Ok(Data { flag: Three { value: Some(123) } })
Alternatively, you could also manually implement Deserialize
for Flag
:
impl<'de> Deserialize<'de> for Flag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
Ok(match value.as_str() {
Some(flag) => match flag {
"one" => Self::One,
"two" => Self::One,
"three" => Self::Three { value: None },
flag => {
return Err(D::Error::custom(format!("unexpected flag {flag:?}")));
}
},
None => {
let three = ThreeFlag::deserialize(value).map_err(D::Error::custom)?;
Self::Three {
value: three.three.value,
}
}
})
}
}
But that might seem a bit more hacky and harder to maintain. Compared to the duplicate code in SimpleFlag
, and the manual From<ComplexFlag> for Flag
implementation.