I have two structs that I want to serialize/deserialize with the tag as a "type"
field in JSON, like so.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
struct ThingA {
value: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
struct ThingB {
value: usize,
}
These serialize as expected. For example,
let a = ThingA { value: 0 };
println!("{}", serde_json::to_string(&a)?);
// This yields the expected result:
// {"type":"ThingA","value":0}
However, I'm running into trouble when I try to add an enum to stand in as a union type for the structs.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum Thing {
ThingA(ThingA),
ThingB(ThingB),
}
The definition above works fine for deserializing JSON, but adds an extra field during serialization.
let json = r#"{"type": "ThingB", "value": 0}"#;
let thing: Thing = serde_json::from_str(json)?;
// Correctly parses to:
// ThingB(ThingB { value: 0 })
println!("{}", serde_json::to_string(&thing)?);
// Incorrectly serializes with an extra "type" field:
// {"type":"ThingB","type":"ThingB","value":0}
Changing #[serde(tag = "type")]
to #[serde(untagged)]
on the Thing
enum causes the opposite problem: Thing
instances serialize properly, but don't get parsed correctly anymore.
My goal is to get JSON {"type": "ThingB", value: 0}
to evaluate to Thing::ThingB(ThingB {value: 0})
during deserialization, and vice versa, but only if I'm deserializing to a Thing
. If I have an unwrapped ThingB
, like ThingB {value: 0}
, I want it to serialize to {"type": "ThingB", value: 0}
as well.
So my questions are: Is there any way to assign the serde tag
or untagged
attributes such that they only apply during serialization/deserialization (similar to serde's rename
)? If not, any advice on how to implement Serialize
and/or Deserialize
to achieve my goal?