Search code examples
rustvariantserde

Serde internally tagged enum with common fields


I have the JSON roughly like this:

[
  {
     "commonA": 1,
     "commonB": 2,
     "type": "Foo",
     "fooSpecificA": 3,
     "fooSpecificB": 4
  },
  {
     "commonA": 5,
     "commonB": 6,
     "type": "Bar",
     "barSpecificA": 7,
     "barSpecificB": 8
  },
  ...

In other words I have internally tagged objects, but some of the fields are common to every type. I'd like to deserialise it to something like this:

struct Entry {
  commonA: i64,
  commonB: i64,
  variant: EntryVariant,
}

enum EntryVariant {
  Foo(FooSpecific),
  Bar(BarSpecific),
}

struct FooSpecific {
  fooSpecificA: i64,
  fooSpecificB: i64,
}

struct BarSpecific {
  barSpecificA: i64,
  barSpecificB: i64,
}

Is that possible with Serde?


Solution

  • Combine internally tagged unions with struct flattening.

    use serde::{Serialize, Deserialize};
    
    #[derive(Debug, Serialize, Deserialize)]
    struct Entry {
        #[serde(rename = "commonA")]
        common_a: i64,
        #[serde(rename = "commonB")]
        common_b: i64,
        #[serde(flatten)]
        variant: EntryVariant,
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    #[serde(tag = "type")]
    enum EntryVariant {
        Foo(FooSpecific),
        Bar(BarSpecific),
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    struct FooSpecific {
        #[serde(rename = "fooSpecificA")]
        foo_specific_a: i64,
        #[serde(rename = "fooSpecificB")]
        foo_specific_b: i64,
    }
    
    #[derive(Debug, Serialize, Deserialize)]
    struct BarSpecific {
        #[serde(rename = "barSpecificA")]
        bar_specific_a: i64,
        #[serde(rename = "barSpecificB")]
        bar_specific_b: i64,
    }
    

    Playground