Skip to content

Instantly share code, notes, and snippets.

@eagletmt
Created February 13, 2022 10:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eagletmt/1613150e1f36caa2ae8eed8d9d64e936 to your computer and use it in GitHub Desktop.
Save eagletmt/1613150e1f36caa2ae8eed8d9d64e936 to your computer and use it in GitHub Desktop.
Deserializing multiple (and optional) internally tagged values
fn main() {
for j in [
r#"{"type": "foo", "subtype": "bar", "a": 1}"#, // Deserialize to Foo(Bar(...))
r#"{"type": "foo", "subtype": "baz", "b": "1"}"#, // Deserialize to Foo(Baz(...))
r#"{"type": "foo", "c": true}"#, // Deserialize to Foo(Qux(...))
r#"{"type": "foo", "subtype": "other", "c": true}"#, // Error
r#"{"type": "hoge", "subtype": "fuga", "d": 2}"#, // Deserialize to Hoge(Fuga(...))
r#"{"type": "hoge", "subtype": "piyo", "e": 3}"#, // Deserialize to Hoge(Piyo(...))
] {
let s = serde_json::from_str::<S>(j);
println!("{:?}", s);
}
}
#[derive(Debug, serde::Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
enum S {
Foo(Foo),
Hoge(Hoge),
}
#[derive(Debug)]
enum Foo {
// when subtype is "bar"
Bar(Bar),
// when subtype is "baz"
Baz(Baz),
// when subtype is missing
Qux(Qux),
}
// https://github.com/serde-rs/serde/issues/1221
impl<'de> serde::Deserialize<'de> for Foo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "snake_case")]
enum Subtype {
Bar,
Baz,
}
#[derive(serde::Deserialize)]
struct SubtypeTagged {
subtype: Option<Subtype>,
#[serde(flatten)]
value: serde_json::Value,
}
let v = SubtypeTagged::deserialize(deserializer)?;
match v.subtype {
Some(Subtype::Bar) => Ok(Foo::Bar(
Bar::deserialize(v.value).map_err(serde::de::Error::custom)?,
)),
Some(Subtype::Baz) => Ok(Foo::Baz(
Baz::deserialize(v.value).map_err(serde::de::Error::custom)?,
)),
None => Ok(Foo::Qux(
Qux::deserialize(v.value).map_err(serde::de::Error::custom)?,
)),
}
}
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Bar {
a: i32,
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Baz {
b: String,
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Qux {
c: bool,
}
#[derive(Debug, serde::Deserialize)]
#[serde(tag = "subtype", rename_all = "snake_case")]
enum Hoge {
Fuga(Fuga),
Piyo(Piyo),
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Fuga {
d: i32,
}
#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct Piyo {
e: i32,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment