Работа с 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())
{ Также в библиотеке
proptest для Rust: полное руководство с примерами
Redis клиент для TypeScript: ioredis и redis (node-redis)
Реактивное программирование в Kotlin: библиотека RxKotlin и корутины