Работа с XML в Rust: библиотеки quick-xml, roxmltree и serde_xml_rs

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с XML в Rust: обзор ключевых библиотек

XML остаётся востребованным форматом для конфигурационных файлов, обмена данными в устаревших системах и хранения структурированной информации. В экосистеме Rust существует несколько зрелых библиотек для работы с XML, каждая из которых ориентирована на определённые сценарии: от высокопроизводительного потокового парсинга до удобной десериализации в строго типизированные структуры. В этой статье мы рассмотрим три основные библиотеки: quick-xml, roxmltree и serde_xml_rs, а также приведём практические примеры их использования.

Библиотека quick-xml: быстрый потоковый парсинг

quick-xml — это одна из самых быстрых библиотек для работы с XML в Rust. Она спроектирована для минимальных накладных расходов и низкого потребления памяти. Основной принцип работы — потоковый парсинг (streaming). Вы читаете XML последовательно, обрабатывая события: открывающие и закрывающие теги, текст, комментарии.

Этот подход идеален для обработки больших файлов (сотни мегабайт), где DOM-модель (загрузка всего документа в память) неприменима. Библиотека поддерживает как чтение, так и запись XML.

Пример: чтение конфигурации с помощью quick-xml

Предположим, у нас есть простой XML-файл config.xml:

<config>
    <app name="my_app">
        <port>8080</port>
        <host>localhost</host>
    </app>
</config>

Парсинг с использованием quick-xml и его высокоуровневого 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(())
}

Как видите, код императивный и требует ручного управления состоянием, но даёт полный контроль над процессом парсинга.

Библиотека roxmltree: работа с DOM-деревом

Если вам удобнее работать с XML как с деревом объектов (DOM), то roxmltree — отличный выбор. Она загружает весь документ в память и предоставляет удобные методы для навигации: поиск по XPath-подобным запросам, обход потомков, получение атрибутов.

Библиотека не поддерживает сериализацию (запись XML), но для чтения и анализа она очень удобна. Важное преимущество — строгая проверка пространств имён (namespaces).

Пример: поиск элементов с помощью 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(xml)?;

    // Поиск всех элементов <title>
    for title in doc.descendants().filter(|n| n.has_tag_name("title")) {
        if let Some(text) = title.text() {
            println!("Title: {}", text);
        }
    }

    // Поиск книги по атрибуту id
    for book in doc.descendants().filter(|n| n.has_tag_name("book")) {
        if let Some(id) = book.attribute("id") {
            if id == "2" {
                if let Some(title) = book.descendants()
                    .find(|n| n.has_tag_name("title"))
                    .and_then(|n| n.text())
                {                    

Рекомендаций