1 unstable release
0.6.0 | Jan 24, 2024 |
---|
#1252 in Encoding
27 downloads per month
Used in 3 crates
(2 directly)
66KB
1.5K
SLoC
serde-xml-rs
xml-rs
based deserializer for Serde (compatible with 1.0)
Example usage
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#"<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);
}
lib.rs
:
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);
}
Dependencies
~0.6–1.3MB
~28K SLoC