use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
enum Animal {
Cat(Option<String>),
Dog(String),
Bird,
}
fn main() {
let json_animals = r#"
[
{"Cat": "Meow"},
"Cat",
{"Dog": "Bark"},
"Bird"
]"#;
println!("{:?}", serde_json::from_str::<Vec<Animal>>(json_animals).unwrap());
}
We basically need a way to deserialize both "Cat"
and {"Cat": "cat_name"}
into Animal
. I know that writing a custom deserializer works but it will be better to have a cleaner solution.
I tried aliasing & renaming & options & defining CatVariant
as a separate enum to include both cases. None of these works because of Error("invalid type: unit variant, expected newtype variant", line: 5, column: 12)
You can do this without a custom Deserialize
implementation, but it will require creating a few extra types to handle the various alternatives present in your schema.
Since we can't directly make Animal
implement Deserialize
for your schema, we can create another type that represents that schema and then convert to Animal
. #[serde(from = "OtherType")]
will allow us to tell serde "deserialize OtherType
and then convert it to this type."
#[derive(Deserialize, Debug)]
#[serde(from = "AnimalRepr")]
enum Animal {
Cat(Option<String>),
Dog(String),
Bird,
}
So what does AnimalRepr
look like? Well, it's either a map or a string. We can used an untagged enum to represent that.
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum AnimalRepr {
Animal(AnimalType),
BareAnimalType(BareAnimalType),
}
So now AnimalType
will handle the maps and BareAnimalType
will handle the strings.
#[derive(Deserialize, Debug)]
enum BareAnimalType {
Cat,
Bird,
}
#[derive(Deserialize, Debug)]
enum AnimalType {
Cat(String),
Dog(String),
}
Now we just need a way to convert AnimalRepr
to Animal
. We can break this conversion apart by having both BareAnimalType
and AnimalType
convertible to Animal
, and delegating to that conversion when converting AnimalRepr
.
impl From<BareAnimalType> for Animal {
fn from(value: BareAnimalType) -> Self {
match value {
BareAnimalType::Cat => Self::Cat(None),
BareAnimalType::Bird => Self::Bird,
}
}
}
impl From<AnimalType> for Animal {
fn from(value: AnimalType) -> Self {
match value {
AnimalType::Cat(n) => Self::Cat(Some(n)),
AnimalType::Dog(n) => Self::Dog(n),
}
}
}
impl From<AnimalRepr> for Animal {
fn from(value: AnimalRepr) -> Self {
match value {
AnimalRepr::Animal(v) => v.into(),
AnimalRepr::BareAnimalType(v) => v.into(),
}
}
}
Putting it all together, we get:
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Debug)]
enum BareAnimalType {
Cat,
Bird,
}
impl From<BareAnimalType> for Animal {
fn from(value: BareAnimalType) -> Self {
match value {
BareAnimalType::Cat => Self::Cat(None),
BareAnimalType::Bird => Self::Bird,
}
}
}
#[derive(Deserialize, Debug)]
enum AnimalType {
Cat(String),
Dog(String),
}
impl From<AnimalType> for Animal {
fn from(value: AnimalType) -> Self {
match value {
AnimalType::Cat(n) => Self::Cat(Some(n)),
AnimalType::Dog(n) => Self::Dog(n),
}
}
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum AnimalRepr {
Animal(AnimalType),
BareAnimalType(BareAnimalType),
}
impl From<AnimalRepr> for Animal {
fn from(value: AnimalRepr) -> Self {
match value {
AnimalRepr::Animal(v) => v.into(),
AnimalRepr::BareAnimalType(v) => v.into(),
}
}
}
#[derive(Deserialize, Debug)]
#[serde(from = "AnimalRepr")]
enum Animal {
Cat(Option<String>),
Dog(String),
Bird,
}
fn main() {
let json_animals = r#"
[
{"Cat": "Meow"},
"Cat",
{"Dog": "Bark"},
"Bird"
]"#;
println!("{:?}", serde_json::from_str::<Vec<Animal>>(json_animals).unwrap());
}
Which outputs:
[Cat(Some("Meow")), Cat(None), Dog("Bark"), Bird]