How to flatten a `Vec` field when serializing a struct with serde?

Viewed 921

I've got some XML that has a tag containing multiple sibling tags with the same name, like so:

<foo>
    <bar/>
    <bar/>
</foo>

(There may also be multiple top level <foo>s as well, though I've not gotten around to trying to (de)serialize that yet.)

Using this code:

use serde::{Deserialize, Serialize};
use quick_xml::de::from_str;
use quick_xml::se::to_string;

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Foo {
    #[serde(rename = "bar", default)]
    bars: Vec<Bar>,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename = "bar")]
struct Bar{

}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn deserialize_trivial_string_test() {
        let fs = "<foo><bar/><bar/></foo>";
        let foo = from_str(fs).unwrap();
        assert_eq!(Foo { bars: vec![Bar {}, Bar {}] }, foo);
    }

    #[test]
    fn serialize_trivial_string_test() {
        let foo = Foo { bars: vec![Bar {}, Bar {}] };
        let fs = to_string(&foo).unwrap();
        assert_eq!("<foo><bar/><bar/></foo>", fs);
    }
}

and don't forget to activate serialize feature:

[dependencies.quick-xml]
version = "0.18.1"
features = ["serialize"]

I can deserialize just fine, but serialization adds in an extra <bar> tag as the bars field wrapping the actual <bar> tags:

<Foo>
    <bar>
        <bar></bar>
        <bar></bar>
    </bar>
</Foo>

The #[serde(rename = "bar", default)] on the bars field was my first attempt to fix the this, based on quick-xml's documentation, but it obviously does not work. I also looked through serde's documentation, and did find a flatten attribute, but it did not seem to work when I tried applying it to the bars field.

Is there any way to get the serialization that I need, short of manually implementing Deserialize?

In case it's relevant, the actual data format I'm trying to work with is the flam3 file format. There's a sample file here. The <flame> element would be the equivalent to <foo> here, and <xform> is equivalent to <bar>.

1 Answers
Related