Serde deserializing a vector of enums

I have a question regarding, the deserializing using the serde_xml_rs crate.

I have some XML that looks like this:

<Spread>
    <SomeField>...</SomeField>
    <SomeField>...</SomeField>
    <A>...</A>
    <B>...</B>
    <C>...</C>
    <D>...</D>
    <A>...</A>
    <D>...</D>
    <C>...</C>
    <B>...</B>
</Spread>

I would like A, B, C and D structs to be contained in a single field in Spread, so I am trying to make a Vector of Enums as follows:

#[derive(Default, Deserialize,Debug)]
#[serde(rename_all="PascalCase")]
pub struct Spread {
    some_fields: Vec<SomeField>,
    #[serde(rename="A",alias="B",alias="C",alias="D")]
    contents: Vec<SpreadContent>
}

#[derive(Default,Deserialize,Debug)]
pub struct SomeField {
    ...
}

#[derive(Deserialize,Debug)]
#[serde(untagged)]
pub enum SpreadContent {
    A(A),
    B(B),
    C(C),
    Other(String),
}

#[derive(Deserialize,Debug)]
pub struct A {
    ...
}

#[derive(Deserialize,Debug)]
pub struct B {
    ...
}

#[derive(Deserialize,Debug)]
pub struct C {
    ...
}

But the compiler complains about Custom `Err` value: Custom { field: "duplicate field `A`"

I am facing this problem multiple places in the XML i am trying to parse, so any help would be greatly appreciated!

The serde-xml-rs crate does not have proper support for Vec or other sequence types, and appears to be missing some other key features as well.

Instead, try using the quick-xml crate with the "serialize" feature enabled. Then, use the special name "$value" in your #[serde(rename)] attribute:

pub struct Spread {
    #[serde(rename = "$value")]
    contents: Vec<SpreadContent>,
}
1 Like

Followup: rename = "$value" won't work if you have other fields alongside the Vec.

You might need to make a single enum for all the immediate children of the <Spread> element, like:

pub enum SpreadContent {
    SomeField(SomeField),
    A(A),
    B(B),
    C(C),
    Other(String),
}

so that struct Spread can have just a single Vec<SpreadContent> field.

Or, implement deserialization manually rather than through serde-derive.

1 Like

Thank you!

As suggested I am now using quick_xml and putting everything into an single vector of enums only with serde-derive. It seems to work if the enum accounts for every possible XML-Child of Spread, but is there any way have SpreadContent default to some value if nothing else matches - like I am trying to do here by implementing the default trait for SpreadContent.

#[derive(Deserialize,Debug)]
pub enum SpreadContent {
    SomeField(SomeField),
    A(A),
    B(B),
    C(C),
    Other
}

impl Default for SpreadContent {
    fn default() -> Self {
        SpreadContent::Other
    }
}

It does not seem to want to default, as the compiler complains about:

Custom("unknown variant `SomeNewChild`, expected one of `SomeField`, `A`, `B`, `C`, `Other`")'

#[serde(other)] might work here. I haven't tested it. You can read about the serde attributes here:

2 Likes

Ah yes, that solves it perfectly. Thank you very much!