
New releaseAug 9, 2026
xmloxide v0.5.0
Eine reine Rust-Neuumsetzung von libxml2
xmloxide
Eine reine Rust-Neuimplementierung von libxml2 — dem De-facto-Standard für XML/HTML-Parsing-Bibliotheken in der Open-Source-Welt.
libxml2 wurde im Dezember 2025 offiziell für ungepflegt erklärt und weist bekannte Sicherheitslücken auf. xmloxide zielt darauf ab, ein speichersicherer, leistungsstarker Ersatz zu sein, der die gleichen Konformitätstestsuiten besteht.
Funktionen
- Speichersicher — Arena-basierter Baum ohne
unsafein der öffentlichen API - Konform — 100% Bestehensquote der W3C-XML-Konformitätstestsuite (1727/1727 anwendbare Tests)
- Fehlerbehandlung — fehlerhafte XML-Daten parsen und trotzdem einen nutzbaren Baum erzeugen, genau wie libxml2
- Mehrere Parsing-APIs — DOM-Baum, SAX2-Streaming, XmlReader-Pull, Push/inkrementell
- HTML-Parser — fehlertolerantes HTML-4.01-Parsen mit automatischem Schließen und void-Elementen
- WHATWG-HTML5-Parser — vollständiger HTML Living Standard-Tokenizer und Baumkonstruktor (8810/8810 html5lib-Tests bestanden)
- HTML5-Streaming — SAX-ähnliche Callback-API für HTML5 (
html5::sax), die den Tokenizer umschließt, ohne einen DOM-Baum zu erstellen - CSS-Selektoren — Abfrage von Elementen mit vertrauter CSS-Syntax (
css::select) inklusive Kombinatoren, Pseudoklassen und schnellem#id-Lookup - XPath 1.0+ — vollständiger Ausdrucksparser und -auswerter mit allen Kernfunktionen von XPath 1.0 sowie wichtigen XPath-2.0-Funktionen (
matches(),replace(),tokenize(),upper-case(),lower-case(),abs(),min(),max()und mehr) - Validierung — DTD-, RelaxNG-, XML-Schema-(XSD)- und ISO-Schematron-Validierung (ISO/IEC 19757-3)
- Serde-Integration — optionales
serde-Feature für XML-(De-)Serialisierung von/nach Rust-Typen - Asynchrones Parsen — optionales
async-Feature zum Parsen vontokio::io::AsyncRead-Quellen - Kanonisches XML — C14N 1.0 und Exclusive-C14N-Serialisierung
- XInclude — Dokumenteninklusionsverarbeitung
- XML-Kataloge — OASIS-XML-Kataloge zur URI-Auflösung
xmllint-CLI — Kommandozeilenwerkzeug zum Parsen, Validieren und Abfragen von XML- Zero-Copy wo möglich — String-Interning für schnelle Vergleiche
- Kein globaler Zustand — jedes
Documentist in sich abgeschlossen undSend + Sync - C/C++-FFI — vollständige C-API mit Header-Datei (
include/xmloxide.h) zur Einbettung in C/C++-Projekte - Minimale Abhängigkeiten — nur
encoding_rs(die Bibliothek hat keine weiteren Abhängigkeiten;clapist nur CLI-spezifisch)
Schnellstart
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");
Serialisierung
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-Abfragen
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);
SAX2-Streaming
use xmloxide::sax::{parse_sax, SaxHandler, DefaultHandler};
use xmloxide::parser::ParseOptions;
struct MyHandler;
impl SaxHandler for MyHandler {
fn start_element(&mut self, name: &str, _: Option<&str>, _: Option<&str>,
_: &[(String, String, Option<String>, Option<String>)]) {
println!("Element: {name}");
}
}
parse_sax("<root><child/></root>", &ParseOptions::default(), &mut MyHandler).unwrap();
HTML-Parsing
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-Selektoren
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-Parsing (WHATWG)
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"));
Fragment-Parsing (der Algorithmus hinter innerHTML) wird ebenfalls unterstützt:
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-Streaming (SAX-ähnlich)
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"]);
Fehlerbehandlung
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);
}
CLI-Werkzeug
# Analysieren und hübsch ausgeben
xmllint --format document.xml
# Gegen ein Schema validieren
xmllint --schema schema.xsd document.xml
xmllint --relaxng schema.rng document.xml
xmllint --schematron schema.sch document.xml
xmllint --dtdvalid schema.dtd document.xml
# XPath-Abfrage
xmllint --xpath "//title" document.xml
# Kanonisches XML
xmllint --c14n document.xml
# HTML parsen
xmllint --html page.html