blob: 41d9c82e1e228b601817a3595758fb182537155b [file] [log] [blame]
//! # 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.
//!
//! ```xml
//! <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.
//!
//! ```yaml
//! - 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
//!
//! ```rust
//! 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
//!
//! ```rust
//! # use serde::{Deserialize, Serialize};
//! # use serde_xml_rs::{from_str, to_string};
//!
//! #[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
//!
//! ```rust
//! # use serde::{Deserialize, Serialize};
//! # use serde_xml_rs::{from_str, to_string};
//!
//! #[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
//!
//! ```rust
//! 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);
//! }
//! ```
//!
pub mod de;
mod error;
pub mod ser;
pub use crate::de::{from_reader, from_str, Deserializer};
pub use crate::error::Error;
pub use crate::ser::{to_string, to_writer, Serializer};
pub use xml::reader::{EventReader, ParserConfig};