Работа с XML в Rust: библиотеки и инструменты
XML (eXtensible Markup Language) остаётся одним из ключевых форматов для обмена данными, конфигурационных файлов и документооборота. Экосистема Rust предлагает несколько зрелых решений для работы с XML — от высокопроизводительного потокового парсинга до удобной сериализации через serde. В этой статье мы разберём три наиболее популярных инструмента: quick-xml, roxmltree и serde-xml-rs.
Почему Rust для работы с XML?
Rust сочетает скорость C/C++ с безопасностью памяти. При обработке больших XML-файлов (например, медицинских записей или геоданных) это критично. Библиотеки для XML в Rust написаны с нуля на Rust (без привязок к C), что гарантирует потокобезопасность и предсказуемую производительность.
Библиотеки для работы с XML в Rust
- quick-xml — высокопроизводительный потоковый парсер (SAX-стиль) и writer. Минимальное потребление памяти, идеален для больших файлов.
- roxmltree — DOM-парсер, строящий дерево документа. Удобен для навигации и модификации.
- serde-xml-rs — сериализация/десериализация структур Rust в/из XML через крейт
serde. Максимальная интеграция с типовой системой.
Установка
Добавьте зависимости в Cargo.toml:
[dependencies]
quick-xml = "0.36" # потоковый парсер
roxmltree = "0.20" # DOM-парсер
serde-xml-rs = "0.6" # сериализация (в паре с serde)
serde = { version = "1.0", features = ["derive"] }
Парсинг XML с помощью quick-xml
quick-xml работает в режиме Reader — последовательно читает токены (открытие тега, текст, закрытие тега). Это экономит память: вы обрабатываете документ по частям, не загружая его целиком.
Пример: чтение RSS-ленты
use quick_xml::Reader;
use quick_xml::events::Event;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let xml = r#"
<rss version="2.0">
<channel>
<title>Новости Rust</title>
<item>
<title>Вышел Rust 1.78</title>
<link>https://example.com/rust-1.78</link>
</item>
</channel>
</rss>"#;
let mut reader = Reader::from_str(xml);
reader.trim_text(true);
let mut buf = Vec::new();
let mut in_item = false;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
if e.name().as_ref() == b"item" {
in_item = true;
}
}
Ok(Event::Text(ref e)) => {
if in_item {
let text = e.unescape()?;
println!("{}", text);
}
}
Ok(Event::End(ref e)) => {
if e.name().as_ref() == b"item" {
in_item = false;
}
}
Ok(Event::Eof) => break,
Err(e) => eprintln!("Ошибка: {}", e),
_ => ()
}
buf.clear();
}
Ok(())
}
Вывод: заголовки всех <item>.
DOM-парсинг с roxmltree
roxmltree загружает весь XML в дерево. Это удобно, когда нужно многократно обращаться к разным узлам или модифицировать структуру.
Пример: извлечение атрибутов и текста
use roxmltree::Document;
fn main() {
let data = r#"
<config version="1.0">
<database>
<host>localhost</host>
<port>5432</port>
</database>
</config>"#;
let doc = Document::parse(data).unwrap();
// Получаем атрибут корневого элемента
if let Some(root) = doc.root_element() {
let version = root.attribute("version").unwrap_or("?");
println!("Версия конфига: {}", version);
}
// Ищем элемент <host>
if let Some(host) = doc.descendants().find(|n| n.has_tag_name("host")) {
let text = host.text().unwrap_or("");
println!("Хост