deser_xml/lib.rs
1//! XML support for deser.
2//!
3//! ```rust
4//! use deser::{Deserialize, Serialize};
5//!
6//! #[derive(Debug, Deserialize, Serialize, PartialEq)]
7//! struct Link {
8//! #[deser(rename = "@href")]
9//! href: String,
10//! #[deser(rename = "@rel")]
11//! rel: Option<String>,
12//! }
13//!
14//! #[derive(Debug, Deserialize, Serialize, PartialEq)]
15//! #[deser(rename = "feed")]
16//! struct Feed {
17//! title: String,
18//! link: Vec<Link>,
19//! count: u32,
20//! }
21//!
22//! let feed: Feed = deser_xml::from_str(r#"
23//! <feed>
24//! <title>Example</title>
25//! <link href="/a"/>
26//! <count>3</count>
27//! <link href="/b" rel="self"/>
28//! </feed>
29//! "#).unwrap();
30//! assert_eq!(feed.title, "Example");
31//! assert_eq!(feed.link.len(), 2);
32//! assert_eq!(feed.link[1].rel.as_deref(), Some("self"));
33//!
34//! assert_eq!(
35//! deser_xml::to_string(&feed).unwrap(),
36//! "<feed><title>Example</title><link href=\"/a\"/>\
37//! <link href=\"/b\" rel=\"self\"/><count>3</count></feed>"
38//! );
39//! ```
40//!
41//! # Data Model
42//!
43//! The document is the value of its root element (the name of the root
44//! element is not checked, see [`Root`]). An element is:
45//!
46//! * **Its text** if it has neither attributes nor child elements:
47//! `<count>3</count>` is `"3"` and `<empty/>` is `""`. Like the values of
48//! query strings, text is passed on as
49//! [lexical atom](deser_core::Atom::Lexical) that the type it's
50//! deserialized into parses. An empty element is `None` for optionals of
51//! types that do not accept the empty text.
52//! * **A map** otherwise. Attributes are entries whose key has the
53//! [attribute prefix](DeserializerConfig::attribute_prefix) (`@href`),
54//! child elements are entries with their name and text is an entry with
55//! the [text key](DeserializerConfig::text_key) (`$text`). Whitespace
56//! between child elements is not text.
57//!
58//! | XML | deser |
59//! |---------------------------------------|-----------------------------------------|
60//! | `<a>1</a>` | `"1"` |
61//! | `<a/>` | `""` |
62//! | `<a href="x"/>` | `{"@href": "x"}` |
63//! | `<a href="x">y</a>` | `{"@href": "x", "$text": "y"}` |
64//! | `<a><b>1</b><c>2</c></a>` | `{"b": "1", "c": "2"}` |
65//! | `<a><b>1</b><c/><b>2</b></a>` | `{"b": "1", "c": "", "b": "2"}` |
66//! | `<p>x <b>y</b> z</p>` | `{"$text": "x ", "b": "y", "$text": " z"}` |
67//!
68//! Maps are [multimaps](deser_core::ContainerShape::with_multimap) whose
69//! order is [significant](deser_core::Order::Significant): an element can
70//! have more than one child with the same name. Fields and map values
71//! that are collections (like `Vec<T>`) collect all of them, also if other
72//! elements are between them. A single child element is a collection of
73//! one value and a missing one an empty collection. For other types
74//! [`DeserializerConfig::duplicate_keys`] decides.
75//!
76//! Whether an element is text or a map depends on the document, the type
77//! it's deserialized into decides what it wants (the text key is the
78//! [key of the content](deser_core::de::ContentKey)):
79//!
80//! * An element with attributes for a type that expects text (like a
81//! `u32`) is its text, the attributes are skipped:
82//! `<count unit="m">3</count>` is `3` for a `u32`.
83//! * An element that is only text for a type that expects a map (like a
84//! struct) is a map with the text under the text key: `<price>3</price>`
85//! is `{"$text": "3"}` for a struct and an empty element an empty map.
86//!
87//! ```rust
88//! #[derive(deser::Deserialize)]
89//! struct Price {
90//! #[deser(rename = "@currency")]
91//! currency: Option<String>,
92//! #[deser(rename = "$text")]
93//! amount: f64,
94//! }
95//!
96//! #[derive(deser::Deserialize)]
97//! struct Item {
98//! price: Vec<Price>,
99//! weight: f64,
100//! }
101//!
102//! let item: Item = deser_xml::from_str(r#"
103//! <item>
104//! <price currency="EUR">3</price>
105//! <price>4</price>
106//! <weight unit="kg">1.5</weight>
107//! </item>
108//! "#).unwrap();
109//! assert_eq!(item.price[0].currency.as_deref(), Some("EUR"));
110//! assert_eq!(item.price[1].amount, 4.0);
111//! assert_eq!(item.weight, 1.5);
112//! ```
113//!
114//! The order of text and child elements is kept by [`Mixed`], which is
115//! for mixed content (`<p>x <b>y</b> z</p>`): every text and child
116//! element is a value, typically of an enum whose variants are named after
117//! the elements:
118//!
119//! ```rust
120//! use deser::Deserialize;
121//! use deser_xml::Mixed;
122//!
123//! #[derive(Debug, Deserialize, PartialEq)]
124//! enum Inline {
125//! #[deser(rename = "$text")]
126//! Text(String),
127//! #[deser(rename = "b")]
128//! Bold(String),
129//! }
130//!
131//! #[derive(Deserialize)]
132//! struct Paragraph {
133//! #[deser(rename = "@class")]
134//! class: Option<String>,
135//! #[deser(flatten)]
136//! content: Mixed<Inline>,
137//! }
138//!
139//! let p: Paragraph =
140//! deser_xml::from_str(r#"<p class="x">x <b>y</b> z</p>"#).unwrap();
141//! assert_eq!(p.content.0, [
142//! Inline::Text("x ".into()),
143//! Inline::Bold("y".into()),
144//! Inline::Text(" z".into()),
145//! ]);
146//! ```
147//!
148//! Enums work like elsewhere: an externally tagged enum is an element with
149//! one child (`<shape><circle r="1"/></shape>`), unit variants are text,
150//! internally tagged enums can use an attribute as tag
151//! (`#[deser(tag = "@type")]`).
152//!
153//! Names are passed on as written (`atom:link`), namespace declarations
154//! (`xmlns` attributes) are not data. The name of the root element and
155//! the namespaces declared on it are not part of the value either, they are
156//! captured by [`Root`]. Values that capture event data (such as
157//! [`Recording`](deser_core::de::Recording)) keep the root element and the
158//! namespace declarations of all elements, so they are written again where
159//! they were. Namespaces can be given prefixes
160//! that are used regardless of the prefixes of the document (see
161//! [`DeserializerConfig::namespaces`]) or be
162//! [resolved](DeserializerConfig::resolve_namespaces) into names like
163//! `{http://www.w3.org/2005/Atom}title` (see [`qname!`] and
164//! [`namespace!`]), which the serializer writes with prefixes.
165//! CDATA sections are text, the
166//! predefined entities (`&`, ...) and character references are
167//! resolved. Entities of document types are never expanded.
168//!
169//! Serializing works the other way around (see [`SerializerConfig`]).
170//!
171//! # Pretty Printing
172//!
173//! By default the output is a single line. [`SerializerConfig::pretty`]
174//! (or [`indent`](SerializerConfig::indent)) writes child elements on
175//! lines of their own, but only where the whitespace is not text: the text
176//! of elements is kept as it is and mixed content stays on a single line.
177//!
178//! ```rust
179//! use deser_xml::{Indent, SerializerConfig};
180//!
181//! #[derive(deser::Serialize)]
182//! #[deser(rename = "item")]
183//! struct Item {
184//! name: &'static str,
185//! tag: Vec<&'static str>,
186//! }
187//!
188//! const PRETTY: SerializerConfig =
189//! SerializerConfig::new().pretty(Indent::Spaces(2));
190//! let item = Item { name: "x", tag: vec!["a", "b"] };
191//! assert_eq!(
192//! PRETTY.to_string(&item).unwrap(),
193//! "<item>\n <name>x</name>\n <tag>a</tag>\n <tag>b</tag>\n</item>"
194//! );
195//! ```
196//!
197//! # Streams
198//!
199//! Documents are read from a [`Read`](std::io::Read) with
200//! [`from_reader`] and written to a [`Write`](std::io::Write) with
201//! [`to_writer`]. The configurations also create readers and writers of
202//! [`deser::io`](deser_core::io) ([`DeserializerConfig::reader`] and
203//! [`SerializerConfig::writer`]), the stream serializer ([`Serializer`])
204//! and deserializer ([`StreamDeserializer`]) work with other kinds of IO
205//! too (for instance async runtimes with `deser-tokio`). A stream holds a
206//! single document. The reader is read to
207//! the end before the document is parsed. The output is written while the
208//! value is serialized: an element is only held back until no more
209//! attributes can come for it, which for structs is known from their fields
210//! and for maps is their end.
211//!
212//! ```rust
213//! # #[cfg(feature = "io")] {
214//! #[derive(deser::Serialize, deser::Deserialize)]
215//! #[deser(rename = "feed")]
216//! struct Feed {
217//! entry: Vec<String>,
218//! }
219//!
220//! let feed = Feed { entry: (0..100).map(|x| x.to_string()).collect() };
221//! let mut out = Vec::new();
222//! deser_xml::to_writer(&mut out, &feed).unwrap();
223//! let read: Feed = deser_xml::from_reader(&out[..]).unwrap();
224//! assert_eq!(read.entry.len(), 100);
225//! # }
226//! ```
227//!
228//! # Features
229//!
230//! * `io` (enabled by default): reading and writing streams of the
231//! standard library, see [streams](#streams).
232//!
233//! # Limitations
234//!
235//! This crate is an early version. The input has to be UTF-8.
236#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
237#![deny(missing_docs)]
238
239mod de;
240mod mixed;
241mod root;
242mod ser;
243mod stream;
244
245pub use self::de::{Deserializer, DeserializerConfig, from_slice, from_str};
246pub use self::mixed::{KeepWhitespace, Mixed, SkipWhitespace, Whitespace};
247pub use self::root::Root;
248#[cfg(feature = "io")]
249pub use self::ser::to_writer;
250pub use self::ser::{Indent, Serializer, SerializerConfig, to_string};
251pub use self::stream::StreamDeserializer;
252#[cfg(feature = "io")]
253pub use self::stream::from_reader;
254
255/// Writes a name in a namespace as `{uri}local`.
256///
257/// This is the notation for names in namespaces (see
258/// [`DeserializerConfig::resolve_namespaces`]). With `@` in front it's
259/// the name of an attribute with the default
260/// [attribute prefix](DeserializerConfig::attribute_prefix). The name is
261/// a literal, so it can be used for `rename`:
262///
263/// ```
264/// use deser_xml::qname;
265///
266/// #[derive(deser::Deserialize)]
267/// struct Link {
268/// #[deser(rename = qname!(@ "http://www.w3.org/1999/xlink", "href"))]
269/// href: String,
270/// }
271///
272/// assert_eq!(qname!("urn:x", "a"), "{urn:x}a");
273/// assert_eq!(qname!(@ "urn:x", "a"), "@{urn:x}a");
274/// ```
275#[macro_export]
276macro_rules! qname {
277 (@ $uri:literal, $local:literal) => {
278 concat!("@{", $uri, "}", $local)
279 };
280 ($uri:literal, $local:literal) => {
281 concat!("{", $uri, "}", $local)
282 };
283}
284
285/// Defines a macro that writes names in a namespace.
286///
287/// `namespace!(atom = "http://www.w3.org/2005/Atom")` defines `atom!` so
288/// that `atom!("title")` is [`qname!("http://www.w3.org/2005/Atom",
289/// "title")`](qname), `atom!(@ "href")` the attribute and `atom!()` the
290/// URI. The names of the macros are the prefixes of the namespaces in
291/// [`prefixes!`]. Like all macros defined by macros, they can be used
292/// after the invocation in the same module and its children.
293///
294/// ```
295/// deser_xml::namespace!(atom = "http://www.w3.org/2005/Atom");
296///
297/// #[derive(deser::Deserialize)]
298/// struct Entry {
299/// #[deser(rename = atom!("title"))]
300/// title: String,
301/// #[deser(rename = atom!(@ "lang"))]
302/// lang: Option<String>,
303/// }
304///
305/// assert_eq!(atom!("title"), "{http://www.w3.org/2005/Atom}title");
306/// assert_eq!(atom!(), "http://www.w3.org/2005/Atom");
307/// ```
308#[macro_export]
309macro_rules! namespace {
310 ($($name:ident = $uri:literal),+ $(,)?) => {
311 $($crate::__namespace!($name, $uri, $);)+
312 };
313}
314
315#[doc(hidden)]
316#[macro_export]
317// rustfmt indents the inner macro further on every run
318#[rustfmt::skip]
319macro_rules! __namespace {
320 ($name:ident, $uri:literal, $d:tt) => {
321 #[allow(unused_macros)]
322 macro_rules! $name {
323 () => {
324 $uri
325 };
326 (@ $d local:literal) => {
327 $crate::qname!(@ $uri, $d local)
328 };
329 ($d local:literal) => {
330 $crate::qname!($uri, $d local)
331 };
332 }
333 };
334}
335
336/// Writes the prefixes of namespaces defined by [`namespace!`].
337///
338/// Every namespace has the name of its macro as prefix unless another one
339/// is given with `as`. The result is the table for
340/// [`SerializerConfig::namespaces`] and
341/// [`DeserializerConfig::namespaces`]:
342///
343/// ```
344/// use deser_xml::{DeserializerConfig, SerializerConfig, prefixes};
345///
346/// deser_xml::namespace!(
347/// atom = "http://www.w3.org/2005/Atom",
348/// dc = "http://purl.org/dc/elements/1.1/",
349/// xlink = "http://www.w3.org/1999/xlink",
350/// );
351///
352/// const PREFIXES: &[(&str, &str)] =
353/// prefixes![atom as "", dc, xlink as "xl"];
354/// assert_eq!(PREFIXES, [
355/// ("", "http://www.w3.org/2005/Atom"),
356/// ("dc", "http://purl.org/dc/elements/1.1/"),
357/// ("xl", "http://www.w3.org/1999/xlink"),
358/// ]);
359///
360/// const WRITE: SerializerConfig =
361/// SerializerConfig::new().namespaces(PREFIXES);
362/// const READ: DeserializerConfig =
363/// DeserializerConfig::new().namespaces(PREFIXES);
364/// ```
365#[macro_export]
366macro_rules! prefixes {
367 ($($name:ident $(as $prefix:literal)?),* $(,)?) => {
368 &[$(($crate::__prefix!($name $(, $prefix)?), $name!())),*]
369 };
370}
371
372#[doc(hidden)]
373#[macro_export]
374macro_rules! __prefix {
375 ($name:ident) => {
376 stringify!($name)
377 };
378 ($name:ident, $prefix:literal) => {
379 $prefix
380 };
381}
382
383/// The names of the special keys and the prefixes of namespaces.
384///
385/// The deserializer publishes them in the state for [`Mixed`].
386#[derive(Debug, Clone, PartialEq, Eq)]
387pub(crate) struct Names {
388 pub(crate) attribute_prefix: &'static str,
389 pub(crate) text_key: &'static str,
390 pub(crate) namespaces: &'static [(&'static str, &'static str)],
391}
392
393impl Names {
394 pub(crate) const fn new() -> Names {
395 Names {
396 attribute_prefix: "@",
397 text_key: "$text",
398 namespaces: &[],
399 }
400 }
401}
402
403impl Default for Names {
404 fn default() -> Names {
405 Names::new()
406 }
407}