Serde: custom variant tagging based on nested field

Hi, I'm trying to deserialize a json like this:

        {
            "field1": {
                "type": "type1",
                "id":"field1-id"
            },
            "att1": true,
            "att2": "A"
        }

Another example:

        {
            "field1": {
                "type": "type2",
                "id":"field1-id"
            },
            "att3": "aaaa",
            "att4": 123
        }

The basic gist of this being: the main json object has always a "field1" object, and one of its properties defines the main object's "type", which in turn determines a number of extra properties.

I thought about implementing with a struct, an enum tagged on "type" with variants to be flattened into the main struct:

pub struct Field1 {
    #[serde(rename = "type")]
    pub field_type: String,
    pub id: String,
}

pub struct MyStruct {
    pub field1: Field1,
    #[serde(flatten)]
    pub variant: Type,
}

#[serde(tag = "type")]
pub enum Type {
    Unknown,
    Type1 {
        att1: bool,
        att2: String,
    },
    Type2 {
        att3: String,
        att4: i32,
    },
}

But of course this doesn't work, as "type" is not part of the enum to be tagged, so my question is, is it possible to provide a custom way of determining the correct variant, or perhaps this approach is completely wrong and there's another way?

Thank you for your help!

Based on your examples and definition of the problem, what you could do is to define the main object's type as an enum, and each variant as a separate struct:


pub enum MyMainModel {
    Type1(Type1Struct),
    Type2(Type2Struct),
}

pub struct Field1 {
    #[serde(rename = "type")]
    pub field_type: Type,
    pub id: String,
}

pub struct Type1Struct {
    pub field1: Field1,
    pub att1: Bool,
    pub att2: String,
}

pub struct Type2Struct {
    pub field1: Field1,
    pub att3: String,
    pub att4: usize,
}

pub enum Type {
  type1,
  type2
}

I guess that gives you an idea of what to do next.

1 Like

Thank you for your reply, your solution actually did work, however while reviewing my code and documentation I ended up using serde(untagged) .

I had tried untagged previously, but that didn't quite work, but then I realized that was a trivial mistake on my side. Untagged tries to deserialize each variant in order: having Unknown with no fields as a first variant means it will always succeed.

I know it's a bit silly on my side, that'll teach me to read the docs more carefully.

Thanks!

Yes, I intentionally left the untagged part out of my post because I was just skimming it out really quick. That's why I thought you would figure out the rest :smiley:.