Rust Serde: Deserialize XML directly to Vec<T>

Viewed 745

I need to use serde to serialize some XML data. The data has a pattern where a tag name in plural (e.g. CATS, DOGS) will contain multiple instances of subtags of the tag in singular (CAT, DOG), like below:

<pets>
  <cats>
    <cat>
      <name>
          cat 1
      </name>
    </cat>
    <cat>
      <name>
          cat 2
      </name>
    </cat>
  </cats>
  <dogs>
    <dog>
      <name>
          dog 1
      </name>
    </dog>
    <dog>
      <name>
          dog 2
      </name>
    </dog>
  </dogs>
</pets>

Now, I can deserialize this with serde and serde-xml-rs:

use serde::{Serialize, Deserialize};
use serde_xml_rs;

#[derive(Debug, Serialize, Deserialize)]
struct Pets {
    pub cats: Cats,
    pub dogs: Dogs,
}

#[derive(Debug, Serialize, Deserialize)]
struct Cats {
    pub cat: Vec<Cat>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Dogs {
    pub dog: Vec<Dog>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Cat {
    name: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Dog {
    name: String,
}


fn main() {
    let s = xml_string_from_above;
    let pets: Pets = serde_xml_rs::from_str(s).unwrap();
    println!("{:#?}", pets);
}

The question: Can I use some serde hack to remove the intermediate structs Cats and Dogs and simply get the multiple instances in a vector?

#[derive(Debug, Serialize, Deserialize)]
struct SlimmedPets {
    pub cats: Vec<Cat>,
    pub dogs: Vec<Dog>,
}

while still getting the same XML back when serializing a SlimmedPets struct?

1 Answers

If your goal is to avoid writing down the intermediate structs like this question, I don't think there's a nice way of doing that.

If your goal is to just get a different struct out of deserialization than the one used to generate the deserializer, you can use the serde(from = "…", and into = "…") attributes.

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(from = "Pets", into = "Pets")]
struct SlimmedPets {
    pub cats: Vec<Cat>,
    pub dogs: Vec<Dog>,
}

impl Into<Pets> for SlimmedPets {
    fn into(self) -> Pets {
        Pets {
            cats: Cats { cat: self.cats },
            dogs: Dogs { dog: self.dogs },
        }
    }
}

impl From<Pets> for SlimmedPets {
    fn from(from: Pets) -> Self {
        SlimmedPets {
            cats: from.cats.cat,
            dogs: from.dogs.dog,
        }
    }
}

let pets: SlimmedPets = serde_xml_rs::from_str(xml_string_from_above).unwrap();
Related