Чистая реализация libxml2 на Rust — де-факто стандартной библиотеки для разбора XML/HTML в мире открытого ПО.
libxml2 перестала официально поддерживаться в декабре 2025 года с известными проблемами безопасности. xmloxide призван стать безопасной по памяти высокопроизводительной заменой, проходящей те же наборы тестов на соответствие.
Возможности
Безопасность памяти — аренное дерево с нулевым unsafe в публичном API
Соответствие стандартам — 100% прохождение тестового набора W3C XML Conformance Test Suite (1727/1727 применимых тестов)
Восстановление после ошибок — разбор некорректного XML с получением используемого дерева, как в libxml2
Несколько API разбора — DOM-дерево, потоковый SAX2, pull-парсер XmlReader, инкрементальный разбор
HTML-парсер — устойчивый к ошибкам парсер HTML 4.01 с авто-закрытием и void-элементами
HTML5-парсер WHATWG — полный токенизатор и построитель дерева по HTML Living Standard (8810/8810 пройденных html5lib-tests)
Потоковый HTML5 — SAX-подобный API обратных вызовов для HTML5 (html5::sax), оборачивающий токенизатор без построения DOM-дерева
CSS-селекторы — запросы элементов с привычным CSS-синтаксисом (css::select) включая комбинаторы, псевдоклассы и быстрый поиск по #id
XPath 1.0+ — полный парсер и вычислитель выражений со всеми базовыми функциями XPath 1.0 и ключевыми функциями XPath 2.0 (matches(), replace(), tokenize(), upper-case(), lower-case(), abs(), min(), max() и другими)
Валидация — DTD, RelaxNG, XML Schema (XSD) и ISO Schematron (ISO/IEC 19757-3)
Интеграция с Serde — опциональная возможность serde для (де)сериализации XML в/из типов Rust
Асинхронный разбор — опциональная возможность async для разбора из tokio::io::AsyncRead
Канонический XML — C14N 1.0 и Exclusive C14N сериализация
XInclude — обработка включения документов
XML-каталоги — OASIS XML Catalogs для разрешения URI
CLI-инструмент xmllint — командная утилита для разбора, валидации и запросов XML
Zero-copy где возможно — интернирование строк для быстрых сравнений
Отсутствие глобального состояния — каждый Document самодостаточен и Send + Sync
C/C++ FFI — полный C-API с заголовочным файлом (include/xmloxide.h) для встраивания в проекты на C/C++
Минимальные зависимости — только encoding_rs (библиотека не имеет других зависимостей; clap используется только в CLI)
Быстрый старт
root@kitploit:~
use xmloxide::Document;
let doc = Document::parse_str("<root><child>Hello</child></root>").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("root"));
assert_eq!(doc.text_content(root), "Hello");
Сериализация
root@kitploit:~
use xmloxide::Document;
use xmloxide::serial::serialize;
let doc = Document::parse_str("<root><child>Hello</child></root>").unwrap();
let xml = serialize(&doc);
assert_eq!(xml, "<root><child>Hello</child></root>");
XPath-запросы
root@kitploit:~
use xmloxide::Document;
use xmloxide::xpath::{evaluate, XPathValue};
let doc = Document::parse_str("<library><book><title>Rust</title></book></library>").unwrap();
let root = doc.root_element().unwrap();
let result = evaluate(&doc, root, "count(book)").unwrap();
assert_eq!(result.to_number(), 1.0);
use xmloxide::html::parse_html;
let doc = parse_html("<p>Hello <br> World").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("html"));
CSS-селекторы
root@kitploit:~
use xmloxide::css::select;
use xmloxide::Document;
let doc = Document::parse_str(r#"<div><p class="intro">Hello</p><p>World</p></div>"#).unwrap();
let root = doc.root_element().unwrap();
let intros = select(&doc, root, "p.intro").unwrap();
assert_eq!(intros.len(), 1);
assert_eq!(doc.text_content(intros[0]), "Hello");
Разбор HTML5 (WHATWG)
root@kitploit:~
use xmloxide::html5::parse_html5;
let doc = parse_html5("<p>Hello <b>world</b>").unwrap();
let root = doc.root_element().unwrap();
assert_eq!(doc.node_name(root), Some("html"));
Также поддерживается разбор фрагментов (алгоритм для innerHTML):
root@kitploit:~
use xmloxide::html5::{parse_html5_with_options, Html5ParseOptions};
let opts = Html5ParseOptions {
scripting: false,
fragment_context: Some("body".to_string()),
};
let doc = parse_html5_with_options("<p>fragment</p>", &opts).unwrap();
Потоковый HTML5 (SAX-подобный)
root@kitploit:~
use xmloxide::html5::sax::{Html5SaxHandler, parse_html5_sax};
struct LinkExtractor { hrefs: Vec<String> }
impl Html5SaxHandler for LinkExtractor {
fn start_element(&mut self, name: &str, attrs: &[(String, String)], _sc: bool) {
if name == "a" {
if let Some((_, href)) = attrs.iter().find(|(n, _)| n == "href") {
self.hrefs.push(href.clone());
}
}
}
}
let mut handler = LinkExtractor { hrefs: Vec::new() };
parse_html5_sax(r#"<a href="https://github.com/jonwiggins/xmloxide/blob/main/page">Link</a>"#, &mut handler);
assert_eq!(handler.hrefs, vec!["/page"]);
Восстановление после ошибок
root@kitploit:~
use xmloxide::parser::{parse_str_with_options, ParseOptions};
let opts = ParseOptions::default().recover(true);
let doc = parse_str_with_options("<root><unclosed>", &opts).unwrap();
for diag in &doc.diagnostics {
eprintln!("{}", diag);
}
Рекурсивный нисходящий парсер XML 1.0 с восстановлением после ошибок
parser::push
Push/инкрементальный парсер для входных данных по частям
html
Устойчивый к ошибкам парсер HTML 4.01
html5
Парсер WHATWG HTML Living Standard (токенизатор + построитель дерева)
html5::sax
Потоковый SAX-подобный API для HTML5 (без построения DOM-дерева)
css
Движок CSS-селекторов для запросов к деревьям документов
sax
Событийный потоковый парсер SAX2
reader
Pull-парсер XmlReader
serial
Сериализаторы XML, HTML и HTML5, а также канонический XML (C14N)
xpath
Парсер и вычислитель выражений XPath 1.0+
validation::dtd
Разбор и валидация DTD
validation::relaxng
Валидация схем RelaxNG
validation::xsd
Валидация XML-схем (XSD)
validation::schematron
Основанная на правилах валидация ISO Schematron
serde_xml
(Де)сериализация Serde XML (опциональная возможность serde)
async_xml
Асинхронный разбор через tokio::io::AsyncRead (опциональная возможность async)
xinclude
Включение документов XInclude 1.0
catalog
OASIS XML Catalogs для разрешения URI
encoding
Обнаружение и перекодировка кодировок символов
Производительность
Пропускная способность разбора конкурентоспособна с libxml2 — в пределах 3-4% на большинстве документов, а на SVG на 12% быстрее. Сериализация работает в 1.5-2.4 раза быстрее благодаря конструкции аренного дерева. XPath работает в 1.1-2.7 раза быстрее по всем бенчмаркам.
Разбор:
Документ
Размер
xmloxide
libxml2
Результат
Atom feed
4.9 KB
26.7 µs (176 MiB/s)
25.5 µs (184 MiB/s)
~4% медленнее
SVG drawing
6.3 KB
58.5 µs (103 MiB/s)
65.6 µs (92 MiB/s)
на 12% быстрее
Maven POM
11.5 KB
76.9 µs (142 MiB/s)
74.2 µs (148 MiB/s)
~4% медленнее
XHTML page
10.2 KB
69.5 µs (139 MiB/s)
61.5 µs (157 MiB/s)
~13% медленнее
Большой (374 KB)
374 KB
2.15 ms (169 MiB/s)
2.08 ms (175 MiB/s)
~3% медленнее
Сериализация:
Документ
Размер
xmloxide
libxml2
Результат
Atom feed
4.9 KB
11.3 µs
17.5 µs
в 1.5x быстрее
Maven POM
11.5 KB
20.1 µs
47.5 µs
в 2.4x быстрее
Большой (374 KB)
374 KB
614 µs
1397 µs
в 2.3x быстрее
XPath:
Выражение
xmloxide
libxml2
Результат
Простой путь (//entry/title)
1.51 µs
1.63 µs
на 8% быстрее
Предикат атрибута (//book[@id])
5.91 µs
15.99 µs
в 2.7x быстрее
Функция count()
1.09 µs
1.67 µs
в 1.5x быстрее
Функция string()
1.32 µs
1.77 µs
в 1.3x быстрее
Ключевые оптимизации: аренное дерево для быстрой сериализации, побайтовые предпроверки для валидации символов, пакетное сканирование текста, быстрые пути ASCII для разбора имён, zero-copy разбиение имён элементов, встраивание разрешения сущностей, слияние шагов // XPath с расширением осей, встраивание аксессоров дерева и быстрые пути проверки имён для осей потомков/потомков.
Интеграционные тесты с реальными XML/HTML-документами, граничными случаями и восстановлением после ошибок
root@kitploit:~
cargo test --all-features
C/C++ FFI
xmloxide предоставляет C-совместимый API для встраивания в проекты на C/C++ (например, Chromium, игровые движки или любые кодовые базы, использующие libxml2).
root@kitploit:~
# Сборка разделяемой + статической библиотек (используется прилагаемый Makefile)
make
# Или сборка по отдельности:
make shared # .so / .dylib / .dll
make static # .a / .lib
# Сборка и запуск примера на C
make example
Полный API — включая навигацию и изменение дерева, вычисление XPath, сериализацию (обычную и с форматированием), разбор HTML/HTML5, валидацию DTD/RelaxNG/XSD/Schematron, C14N, потоковый SAX, XmlReader, push-парсер и XML-каталоги — объявлен в include/xmloxide.h.
Миграция с libxml2
libxml2
xmloxide (Rust)
xmloxide (C FFI)
xmlReadMemory
Document::parse_str
xmloxide_parse_str
xmlReadFile
Document::parse_file
xmloxide_parse_file
xmlParseDoc
Document::parse_bytes
xmloxide_parse_bytes
htmlReadMemory
html::parse_html
xmloxide_parse_html
(разбор HTML5)
html5::parse_html5
—
(фрагмент HTML5 / innerHTML)
html5::parse_html5_with_options
—
(потоковый HTML5)
html5::sax::parse_html5_sax
—
(CSS-селекторы / querySelector)
css::select
—
xmlFreeDoc
(удалить Document)
xmloxide_free_doc
xmlDocGetRootElement
doc.root_element()
xmloxide_doc_root_element
xmlNodeGetContent
doc.text_content(id)
xmloxide_node_text_content
xmlNodeSetContent
doc.set_text_content(id, s)
xmloxide_set_text_content
xmlGetProp
doc.attribute(id, name)
xmloxide_node_attribute
xmlSetProp
doc.set_attribute(...)
xmloxide_set_attribute
xmlNewNode
doc.create_node(...)
Потокобезопасность: В отличие от libxml2, xmloxide не имеет глобального состояния. Каждый Document самодостаточен и Send + Sync. Уровень FFI использует локальное хранилище потока для последнего сообщения об ошибке — у каждого потока собственное состояние ошибки. Функции инициализации или очистки не требуются.
Фаззинг
xmloxide включает цели для фаззинга при тестировании безопасности:
root@kitploit:~
# Установка cargo-fuzz (требуется nightly)
cargo install cargo-fuzz
# Запуск цели фаззинга
cargo +nightly fuzz run fuzz_xml_parse
cargo +nightly fuzz run fuzz_html_parse
cargo +nightly fuzz run fuzz_html5_parse
cargo +nightly fuzz run fuzz_html5_fragment
cargo +nightly fuzz run fuzz_xpath
cargo +nightly fuzz run fuzz_roundtrip
cargo +nightly fuzz run fuzz_sax
cargo +nightly fuzz run fuzz_reader
cargo +nightly fuzz run fuzz_push
cargo +nightly fuzz run fuzz_validation
cargo +nightly fuzz run fuzz_schematron
Нет XML 1.1 — xmloxide реализует только XML 1.0 (пятое издание). XML 1.1 редко используется и не планируется.
Нет XSLT — XSLT — отдельная спецификация (libxslt) и выходит за рамки.
HTML-парсеры — предоставлены как парсер HTML 4.01 (поведение, соответствующее libxml2), так и полный парсер WHATWG HTML5. Парсер HTML5 проходит 100% html5lib-tests.
Push-парсер буферизует внутри — API push/инкрементального парсера (PushParser) в настоящее время буферизует все переданные данные и выполняет полный разбор при вызове finish(), а не потоковую передачу, как xmlParseChunk в libxml2. Потоковый SAX (parse_sax для XML, html5::sax::parse_html5_sax для HTML5) доступен как альтернатива для обработки больших документов с ограниченной памятью.
Ось namespace:: в XPath — ось namespace:: возвращает узел элемента, когда совпадают области видимости пространств имён (вместо создания отдельных узлов пространств имён), следуя той же схеме, что и ось атрибутов.
Участие
См. CONTRIBUTING.md для настройки разработки и рекомендаций.