# quick-xml examples There are many ways to use quick-xml, this guide is intended to help you choose *which* of its APIs to use. There are two, from highest-level to lowest: - **serde** (the `serialize` feature) — map XML directly onto `#[derive(Serialize, Deserialize)]` structs. Best when your XML corresponds cleanly to Rust types. - **the pull `Reader` and push `Writer`** — work with a stream of `Event`s (`Start`, `Text`, `End`, ...). Best when you need streaming, partial parsing, or to transform a document. The `Writer` additionally offers a high-level builder and a low-level event API; see [Writing](#writing) below. For simple jobs, it's best to start with serde and drop to the `Reader`/`Writer` only when it doesn't fit. For parsing especially large or complex documents however, using the lower-level APIs is likely to be a better approach. ## Which approach should I use? ### Reading The rule of thumb, in order of preference: 1. **serde** — if the document is reasonably sized, performance is not critical, and the shape maps onto structs, this is the least code by far. You describe the types once and get parsing for free. See [`serde_roundtrip.rs`](serde_roundtrip.rs) 2. **A state machine** — when the items you want are simple and not too deeply nested, a hand-written `Reader` loop that tracks its position in an explicit `enum` is fast, allocation-light, and makes the grammar you accept obvious. Matching on `(state, event)` pairs shows exactly what you expect where, and a single read buffer serves the whole document. See [`reader_patterns.rs`](reader_patterns.rs) 3. **Nested readers / split functions** — when the document has many levels, handing each subtree to its own function reads more naturally than one giant state enum. The trade-off is that it is harder to reuse a single read buffer across levels, so deeply nested parsing can allocate more. See [`reader_patterns.rs`](reader_patterns.rs), [`nested_readers.rs`](nested_readers.rs) In practice case 1 is far more common than 2, which is far more common than 3. Reach for the lower-level options mainly when serde can't express the mapping, when you are streaming data too large to hold in memory, or when parsing is hot enough that avoiding the intermediate structs matters. If more than one part of your program needs the same document, or if you want the parsing logic to live apart from what consumes it — layer a **visitor** over any of the above: one driver walks the document and calls back into a trait, and each consumer implements only the callbacks it needs. This is a powerful design pattern that can greatly simplify code re-use in the future. See [`visitor.rs`](visitor.rs) ### Writing 1. **serde** — if you already have the data in Rust structs, serializing them directly is the least effort. See [`serde_roundtrip.rs`](serde_roundtrip.rs) 2. **The high-level `Writer::create_element` builder** — an extension of the standard `Writer` API which handles writing the opening and closing element tags and makes adding attributes more natural. A closure is provided for writing the inner content of the element. See [`writer.rs`](writer.rs) 3. **Low-level `Writer::write_event`** — emit each `Start`/`Text`/`End` event yourself. Most verbose and easiest to unbalance, but gives total control and is ideal when *transforming* a document (read an event, tweak it, write it back out). Sees [`writer.rs`](writer.rs), and the transform example in the crate's [README](../README.md) ## Buffered vs. borrowed reading - `Reader::from_str` / `Reader::from_reader` over a `&[u8]` can return events that **borrow** from the input, so you call `read_event()` with no buffer. - Streaming sources (files, sockets) use `read_event_into(&mut buf)` and write into a `Vec` you supply. Reusing (and `clear()`-ing) that one buffer across the loop keeps allocations low. See [`read_buffered.rs`](read_buffered.rs) ## The examples ### Start here - [`getting_started.rs`](getting_started.rs) — the canonical pull-reader loop: create a reader, match events, pull out attributes and text. **Read this first.** ### Reading - [`reader_patterns.rs`](reader_patterns.rs) — state machine vs. nested readers, side by side, parsing the same document into the same result. - [`visitor.rs`](visitor.rs) — the visitor pattern: one parser driver, a trait of callbacks, and several consumers that each override only what they need. - [`read_buffered.rs`](read_buffered.rs) — streaming with a reusable buffer. - [`nested_readers.rs`](nested_readers.rs) — walking several levels to extract data from a real-world (ECMA-376) document. - [`read_nodes.rs`](read_nodes.rs) — dispatching on top-level nodes by hand. - [`custom_entities.rs`](custom_entities.rs) — resolving custom `&entity;` definitions. - [`read_utf16.rs`](read_utf16.rs) — non-UTF-8 input via `DecodingReader` (needs `--features encoding`). ### Writing - [`writer.rs`](writer.rs) — the high-level builder and the low-level event API, producing identical output. ### serde - [`serde_roundtrip.rs`](serde_roundtrip.rs) — mapping XML to structs and back: attributes (`@`), text (`$text`), nesting, sequences, and type conversion. **Read this to learn the serde field-naming rules** (needs `--features serialize`). - [`read_nodes_serde.rs`](read_nodes_serde.rs) — the serde counterpart to `read_nodes.rs` (needs `--features serialize`). - [`flattened_enum.rs`](flattened_enum.rs) — choosing an enum variant from an attribute with a custom (de)serializer (needs `--features serialize`). ## Running an example ```console cargo run --example getting_started cargo run --example serde_roundtrip --features serialize cargo run --example read_utf16 --features encoding ```