Uma reimplementação pura em Rust do libxml2 — a biblioteca de análise XML/HTML padrão de facto no mundo open-source.
O libxml2 tornou-se oficialmente não mantido em dezembro de 2025, com problemas de segurança conhecidos. O xmloxide visa ser um substituto seguro em termos de memória e de alto desempenho que passa nos mesmos conjuntos de testes de conformidade.
Funcionalidades
Seguro em termos de memória — árvore baseada em arena com zero unsafe na API pública
Conforme — taxa de aprovação de 100% no W3C XML Conformance Test Suite (1727/1727 testes aplicáveis)
Recuperação de erros — analisa XML malformado e ainda produz uma árvore utilizável, tal como o libxml2
Analisador HTML — análise HTML 4.01 tolerante a erros com fecho automático e elementos vazios
Analisador HTML5 WHATWG — tokenizer e construtor de árvore completos do HTML Living Standard (8810/8810 testes html5lib-tests aprovados)
Streaming HTML5 — API de callback semelhante a SAX para HTML5 (html5::sax) que encapsula o tokenizer sem construir uma árvore DOM
Seletores CSS — consulta elementos com sintaxe CSS familiar (css::select) incluindo combinadores, pseudo-classes e pesquisa rápida por #id
XPath 1.0+ — analisador de expressões e avaliador completos com todas as funções principais XPath 1.0, mais funções chave do XPath 2.0 (matches(), replace(), tokenize(), upper-case(), lower-case(), abs(), min(), max() e mais)
Validação — validação DTD, RelaxNG, XML Schema (XSD) e ISO Schematron (ISO/IEC 19757-3)
Integração Serde — funcionalidade opcional serde para (de)serialização XML de/para tipos Rust
Análise assíncrona — funcionalidade opcional async para análise a partir de fontes tokio::io::AsyncRead
XML Canónico — serialização C14N 1.0 e C14N Exclusiva
XInclude — processamento de inclusão de documentos
Catálogos XML — Catálogos XML OASIS para resolução de URI
CLI xmllint — ferramenta de linha de comando para analisar, validar e consultar XML
Zero-copy sempre que possível — interning de strings para comparações rápidas
Sem estado global — cada Document é autónomo e Send + Sync
FFI C/C++ — API C completa com ficheiro de cabeçalho (include/xmloxide.h) para incorporação em projetos C/C++
Dependências mínimas — apenas encoding_rs (a biblioteca não tem outras dependências; clap é apenas para CLI)
Início Rápido
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");
Serialização
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>");
Consultas 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"));
Seletores 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");
Análise 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"));
A análise de fragmentos (o algoritmo por detrás do innerHTML) também é suportada:
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();
Streaming HTML5 (semelhante a 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"]);
Recuperação de Erros
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);
}
Ferramenta CLI
root@kitploit:~
# Analisar e imprimir formatado
xmllint --format document.xml
# Validar contra um esquema
xmllint --schema schema.xsd document.xml
xmllint --relaxng schema.rng document.xml
xmllint --schematron schema.sch document.xml
xmllint --dtdvalid schema.dtd document.xml
# Consulta XPath
xmllint --xpath "//title" document.xml
# XML Canónico
xmllint --c14n document.xml
# Analisar HTML
xmllint --html page.html
Visão Geral dos Módulos
Módulo
Descrição
tree
Árvore DOM baseada em arena (Document, NodeId, NodeKind)
parser
Analisador descendente recursivo XML 1.0 com recuperação de erros
parser::push
Analisador push/incremental para entrada fragmentada
html
Analisador HTML 4.01 tolerante a erros
html5
Analisador HTML Living Standard WHATWG (tokenizer + construtor de árvore)
html5::sax
API de streaming semelhante a SAX para HTML5 (sem construção de árvore DOM)
css
Motor de seletores CSS para consulta de árvores de documentos
sax
Analisador orientado por eventos de streaming SAX2
reader
API de análise pull baseada em XmlReader
serial
Serializadores XML, HTML e HTML5, mais XML Canónico (C14N)
xpath
Analisador de expressões e avaliador XPath 1.0+
validation::dtd
Análise e validação DTD
validation::relaxng
Validação de esquemas RelaxNG
validation::xsd
Validação XML Schema (XSD)
validation::schematron
Validação baseada em regras ISO Schematron
serde_xml
(De)serialização Serde XML (funcionalidade opcional serde)
async_xml
Análise assíncrona via tokio::io::AsyncRead (funcionalidade opcional async)
xinclude
Inclusão de documentos XInclude 1.0
catalog
Catálogos XML OASIS para resolução de URI
encoding
Desempenho
A taxa de transferência de análise é competitiva com o libxml2 — dentro de 3-4% na maioria dos documentos, e 12% mais rápida em SVG. A serialização é 1,5-2,4x mais rápida graças ao design da árvore baseada em arena. O XPath é 1,1-2,7x mais rápido em todos os benchmarks.
Análise:
Documento
Tamanho
xmloxide
libxml2
Resultado
Atom feed
4.9 KB
26.7 µs (176 MiB/s)
25.5 µs (184 MiB/s)
~4% mais lento
Desenho SVG
6.3 KB
58.5 µs (103 MiB/s)
65.6 µs (92 MiB/s)
12% mais rápido
POM Maven
11.5 KB
76.9 µs (142 MiB/s)
74.2 µs (148 MiB/s)
~4% mais lento
Página XHTML
10.2 KB
69.5 µs (139 MiB/s)
61.5 µs (157 MiB/s)
~13% mais lento
Grande (374 KB)
374 KB
2.15 ms (169 MiB/s)
2.08 ms (175 MiB/s)
~3% mais lento
Serialização:
Documento
Tamanho
xmloxide
libxml2
Resultado
Atom feed
4.9 KB
11.3 µs
17.5 µs
1,5x mais rápido
POM Maven
11.5 KB
20.1 µs
47.5 µs
2,4x mais rápido
Grande (374 KB)
374 KB
614 µs
1397 µs
2,3x mais rápido
XPath:
Expressão
xmloxide
libxml2
Resultado
Caminho simples (//entry/title)
1.51 µs
1.63 µs
8% mais rápido
Predicado de atributo (//book[@id])
5.91 µs
15.99 µs
2,7x mais rápido
Função count()
1.09 µs
1.67 µs
1,5x mais rápido
Função string()
1.32 µs
1.77 µs
1,3x mais rápido
Otimizações principais: árvore baseada em arena para serialização rápida, pré-verificações ao nível do byte para validação de caracteres, leitura em massa de texto, caminhos rápidos ASCII para análise de nomes, divisão de nomes de elementos sem cópia, resolução inline de entidades, fusão de passos // do XPath com expansão de eixos fundidos, acessores de árvore inline e caminhos rápidos de teste de nome para eixos child/descendant.
root@kitploit:~
# Executar benchmarks (requer biblioteca de sistema libxml2)
cargo bench --features bench-libxml2 --bench comparison_bench
Testes
1078 testes unitários em todos os módulos
138 testes FFI cobrindo toda a superfície da API C (incluindo SAX, Schematron e CSS)
Suite de compatibilidade libxml2 — 119/119 testes aprovados (100%) cobrindo análise XML, namespaces, deteção de erros e análise HTML
W3C XML Conformance Test Suite — 1727/1727 testes aplicáveis aprovados (100%)
html5lib-tests — 7032/7032 testes de tokenizer + 1778/1778 testes de construção de árvore (100%)
Testes de integração cobrindo documentos XML/HTML do mundo real, casos extremos e recuperação de erros
root@kitploit:~
cargo test --all-features
FFI C/C++
O xmloxide fornece uma API compatível com C para incorporação em projetos C/C++ (como Chromium, motores de jogo ou qualquer base de código que atualmente use libxml2).
root@kitploit:~
# Construir bibliotecas partilhada + estática (usa o Makefile incluído)
make
# Ou construir individualmente:
make shared # .so / .dylib / .dll
make static # .a / .lib
# Construir e executar o exemplo em C
make example
A API completa — incluindo navegação e mutação de árvore, avaliação XPath, serialização (simples e formatada), análise HTML/HTML5, validação DTD/RelaxNG/XSD/Schematron, C14N, streaming SAX, XmlReader, analisador push e Catálogos XML — é declarada em include/xmloxide.h.
Migração do 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
(Análise HTML5)
html5::parse_html5
—
(Fragmento HTML5 / innerHTML)
html5::parse_html5_with_options
—
(Streaming HTML5)
html5::sax::parse_html5_sax
—
(Seletores CSS / querySelector)
css::select
—
xmlFreeDoc
(descarte o 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(...)
Segurança em threads: Ao contrário do libxml2, o xmloxide não possui estado global. Cada Document é autónomo e Send + Sync. A camada FFI utiliza armazenamento local de thread para a última mensagem de erro — cada thread tem o seu próprio estado de erro. Nenhuma função de inicialização ou limpeza é necessária.
Fuzzing
O xmloxide inclui alvos de fuzzing para testes de segurança:
root@kitploit:~
# Instalar cargo-fuzz (requer nightly)
cargo install cargo-fuzz
# Executar um alvo de fuzzing
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
Sem XML 1.1 — o xmloxide implementa apenas XML 1.0 (Quinta Edição). O XML 1.1 raramente é usado e não está planeado.
Sem XSLT — XSLT é uma especificação separada (libxslt) e está fora do âmbito.
Analisadores HTML — são fornecidos um analisador HTML 4.01 (compatível com o comportamento do libxml2) e um analisador HTML5 WHATWG completo. O analisador HTML5 passa 100% dos testes html5lib-tests.
O analisador push faz buffer internamente — a API do analisador push/incremental (PushParser) atualmente faz buffer de todos os dados enviados e realiza a análise completa no finish(), em vez de fazer streaming real como o xmlParseChunk do libxml2. O streaming SAX (parse_sax para XML, html5::sax::parse_html5_sax para HTML5) está disponível como alternativa para processamento de grandes documentos com restrição de memória.
Eixo XPath namespace:: — o eixo namespace:: retorna o nó do elemento quando os namespaces em âmbito coincidem (em vez de materializar nós de namespace separados), seguindo o mesmo padrão do eixo de atributos.
Contribuir
Consulte CONTRIBUTING.md para configuração de desenvolvimento e diretrizes.
Changelog
Consulte CHANGELOG.md para o histórico de versões.
Licença
MIT
Deteção e transcodificação de codificação de caracteres