7

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
    I was asking myself the same question and went the direction of: directly replicate the data structure to be deserialized and then convert it to whatever you want to work with. Another way would be to deserialize the data yourself but custom serializations are quite involved and maybe you are just better off with this solution. Watching in case anyone comes up with a solution. – Beat Scherrer Aug 15 '21 at 14:35

1 Answers1

0

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();
Caesar
  • 6,733
  • 4
  • 38
  • 44