Expand description

Serde XML

XML is a flexible markup language that is still used for sharing data between applications or for writing configuration files.

Serde XML provides a way to convert between text and strongly-typed Rust data structures.

Caveats

The Serde framework was mainly designed with formats such as JSON or YAML in mind. As opposed to XML, these formats have the advantage of a stricter syntax which makes it possible to know what type a field is without relying on an accompanying schema, and disallows repeating the same tag multiple times in the same object.

For example, encoding the following document in YAML is not trivial.

<document>
  <header>A header</header>
  <section>First section</section>
  <section>Second section</section>
  <sidenote>A sidenote</sidenote>
  <section>Third section</section>
  <sidenote>Another sidenote</sidenote>
  <section>Fourth section</section>
  <footer>The footer</footer>
</document>

One possibility is the following YAML document.

- header: A header
- section: First section
- section: Second section
- sidenote: A sidenote
- section: Third section
- sidenote: Another sidenote
- section: Fourth section
- footer: The footer

Other notable differences:

  • XML requires a named root node.
  • XML has a namespace system.
  • XML distinguishes between attributes, child tags and contents.
  • In XML, the order of nodes is sometimes important.

Basic example

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

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

fn main() {
    let src = r#"<?xml version="1.0" encoding="UTF-8"?><Item><name>Banana</name><source>Store</source></Item>"#;
    let should_be = Item {
        name: "Banana".to_string(),
        source: "Store".to_string(),
    };

    let item: Item = from_str(src).unwrap();
    assert_eq!(item, should_be);

    let reserialized_item = to_string(&item).unwrap();
    assert_eq!(src, reserialized_item);
}

Tag contents


#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Document {
    content: Content
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Content {
    #[serde(rename = "$value")]
    value: String
}

fn main() {
    let src = r#"<document><content>Lorem ipsum</content></document>"#;
    let document: Document = from_str(src).unwrap();
    assert_eq!(document.content.value, "Lorem ipsum");
}

Repeated tags


#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct PlateAppearance {
    #[serde(rename = "$value")]
    events: Vec<Event>
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
enum Event {
    Pitch(Pitch),
    Runner(Runner),
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Pitch {
    speed: u32,
    r#type: PitchType,
    outcome: PitchOutcome,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum PitchType { FourSeam, TwoSeam, Changeup, Cutter, Curve, Slider, Knuckle, Pitchout }

#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum PitchOutcome { Ball, Strike, Hit }

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Runner {
    from: Base, to: Option<Base>, outcome: RunnerOutcome,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Base { First, Second, Third, Home }
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum RunnerOutcome { Steal, Caught, PickOff }

fn main() {
    let document = r#"
        <plate-appearance>
          <pitch speed="95" type="FourSeam" outcome="Ball" />
          <pitch speed="91" type="FourSeam" outcome="Strike" />
          <pitch speed="85" type="Changeup" outcome="Ball" />
          <runner from="First" to="Second" outcome="Steal" />
          <pitch speed="89" type="Slider" outcome="Strike" />
          <pitch speed="88" type="Curve" outcome="Hit" />
        </plate-appearance>"#;
    let plate_appearance: PlateAppearance = from_str(document).unwrap();
    assert_eq!(plate_appearance.events[0], Event::Pitch(Pitch { speed: 95, r#type: PitchType::FourSeam, outcome: PitchOutcome::Ball }));
}

Custom EventReader

use serde::{Deserialize, Serialize};
use serde_xml_rs::{from_str, to_string, de::Deserializer};
use xml::reader::{EventReader, ParserConfig};

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

fn main() {
    let src = r#"<Item><name>  Banana  </name><source>Store</source></Item>"#;
    let should_be = Item {
        name: "  Banana  ".to_string(),
        source: "Store".to_string(),
    };

    let config = ParserConfig::new()
        .trim_whitespace(false)
        .whitespace_to_characters(true);
    let event_reader = EventReader::new_with_config(src.as_bytes(), config);
    let item = Item::deserialize(&mut Deserializer::new(event_reader)).unwrap();
    assert_eq!(item, should_be);
}

Re-exports

pub use crate::de::from_reader;
pub use crate::de::from_str;
pub use crate::de::Deserializer;
pub use crate::ser::to_string;
pub use crate::ser::to_writer;
pub use crate::ser::Serializer;

Modules

Macros

Structs

A wrapper around an std::io::Read instance which provides pull-based XML parsing.

Parser configuration structure.

Enums