Working with XML in Rust: overview of key libraries
XML remains a popular format for configuration files, data exchange in legacy systems, and storing structured information. In the Rust ecosystem, there are several mature libraries for working with XML, each oriented towards specific scenarios: from high-performance streaming parsing to convenient deserialization into strictly typed structures. In this article, we will look at three main libraries: quick-xml, roxmltree, and serde_xml_rs, and provide practical examples of their use.
quick-xml library: fast streaming parsing
quick-xml is one of the fastest libraries for working with XML in Rust. It is designed for minimal overhead and low memory consumption. The main principle of operation is streaming parsing. You read XML sequentially, processing events: opening and closing tags, text, comments.
This approach is ideal for processing large files (hundreds of megabytes) where the DOM model (loading the entire document into memory) is not applicable. The library supports both reading and writing XML.
Example: reading a configuration with quick-xml
Suppose we have a simple XML file config.xml:
<config> <app name="my_app"> <port>8080</port> <host>localhost</host> </app></config>Parsing using quick-xml and its high-level API Reader:
use quick_xml::Reader;use quick_xml::events::Event;
fn main() -> Result<(), Box<dyn std::error::Error>> { let xml = r#" <config> <app name="my_app"> <port>8080</port> <host>localhost</host> </app> </config> "#;
let mut reader = Reader::from_str(xml); let mut txt = Vec::new(); let mut in_port = false;
loop { match reader.read_event() { Ok(Event::Start(ref e)) => { if e.name().as_ref() == b"port" { in_port = true; } } Ok(Event::Text(ref e)) => { if in_port { txt.push(e.unescape()?.to_string()); in_port = false; } } Ok(Event::Eof) => break, Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => () } } println!("Port value: {}", txt[0]); Ok(())}As you can see, the code is imperative and requires manual state management, but it gives full control over the parsing process.
roxmltree library: working with the DOM tree
If you prefer to work with XML as a tree of objects (DOM), then roxmltree is an excellent choice. It loads the entire document into memory and provides convenient methods for navigation: searching with XPath-like queries, traversing descendants, getting attributes.
The library does not support serialization (writing XML), but it is very convenient for reading and analysis. An important advantage is strict namespace checking.
Example: searching for elements with roxmltree
use roxmltree::Document;
fn main() -> Result<(), Box<dyn std::error::Error>> { let xml = r#" <catalog> <book id="1"> <title>Rust in Action</title> <author>Tim McNamara</author> </book> <book id="2"> <title>Programming Rust</title> <author>Jim Blandy</author> </book> </catalog> "#;
let doc = Document::parse(x