Skip to content

Instantly share code, notes, and snippets.

@eagletmt
Created February 13, 2022 10:45
Show Gist options
  • Select an option

  • Save eagletmt/1613150e1f36caa2ae8eed8d9d64e936 to your computer and use it in GitHub Desktop.

Select an option

Save eagletmt/1613150e1f36caa2ae8eed8d9d64e936 to your computer and use it in GitHub Desktop.

Revisions

  1. eagletmt created this gist Feb 13, 2022.
    102 changes: 102 additions & 0 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    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,
    }