
libxml2 का एक शुद्ध Rust पुनर्कार्यान्वयन
# xmloxide
[](https://github.com/jonwiggins/xmloxide/actions/workflows/ci.yml)
[](https://crates.io/crates/xmloxide)
[](https://docs.rs/xmloxide)
[](LICENSE)
[](https://www.rust-lang.org)
libxml2 का एक शुद्ध Rust पुनःकार्यान्वयन — जो ओपन-सोर्स दुनिया में डी-फैक्टो मानक XML/HTML पार्सिंग लाइब्रेरी है।
libxml2 दिसंबर 2025 में आधिकारिक रूप से अनुरक्षित नहीं रह गया था, जिसमें ज्ञात सुरक्षा समस्याएँ हैं। xmloxide एक मेमोरी-सुरक्षित, उच्च-प्रदर्शन वाला प्रतिस्थापन बनने का लक्ष्य रखता है जो समान अनुरूपता परीक्षण सूट पास करता है।
## विशेषताएँ
- **मेमोरी-सुरक्षित** — सार्वजनिक API में शून्य `unsafe` के साथ एरेना-आधारित ट्री
- **अनुरूप** — W3C XML अनुरूपता परीक्षण सूट पर 100% पास दर (1727/1727 लागू परीक्षण)
- **त्रुटि पुनर्प्राप्ति** — libxml2 की तरह ही दूषित XML को पार्स करें और उपयोगी ट्री प्राप्त करें
- **एकाधिक पार्सिंग API** — DOM ट्री, SAX2 स्ट्रीमिंग, XmlReader पुल, पुश/इंक्रीमेंटल
- **HTML पार्सर** — ऑटो-क्लोज़िंग और वॉयड एलिमेंट के साथ त्रुटि-सहिष्णु HTML 4.01 पार्सिंग
- **WHATWG HTML5 पार्सर** — पूर्ण [HTML लिविंग स्टैंडर्ड](https://html.spec.whatwg.org/) टोकनाइज़र और ट्री बिल्डर (8810/8810 html5lib-tests पास)
- **HTML5 स्ट्रीमिंग** — HTML5 (`html5::sax`) के लिए SAX-जैसी कॉलबैक API जो DOM ट्री बनाए बिना टोकनाइज़र को लपेटती है
- **CSS सेलेक्टर** — कॉम्बिनेटर, स्यूडो-क्लास और तेज़ `#id` लुकअप सहित परिचित CSS सिंटैक्स (`css::select`) से एलिमेंट क्वेरी करें
- **XPath 1.0+** — सभी XPath 1.0 कोर फंक्शन और प्रमुख XPath 2.0 फंक्शन (`matches()`, `replace()`, `tokenize()`, `upper-case()`, `lower-case()`, `abs()`, `min()`, `max()`, और अधिक) के साथ पूर्ण एक्सप्रेशन पार्सर और मूल्यांकक
- **सत्यापन** — DTD, RelaxNG, XML स्कीमा (XSD), और ISO Schematron (ISO/IEC 19757-3) सत्यापन
- **Serde एकीकरण** — वैकल्पिक `serde` फीचर XML के Rust टाइपों में (डी)सीरियलाइज़ेशन के लिए
- **एसिंक पार्सिंग** — वैकल्पिक `async` फीचर `tokio::io::AsyncRead` स्रोतों से पार्सिंग के लिए
- **कैननिकल XML** — C14N 1.0 और Exclusive C14N सीरियलाइज़ेशन
- **XInclude** — दस्तावेज़ समावेशन प्रसंस्करण
- **XML कैटलॉग** — URI रिज़ॉल्यूशन के लिए OASIS XML कैटलॉग
- **`xmllint` CLI** — XML को पार्स करने, सत्यापित करने और क्वेरी करने के लिए कमांड-लाइन टूल
- **जहाँ संभव हो जीरो-कॉपी** — तेज़ तुलना के लिए स्ट्रिंग इंटर्निंग
- **कोई वैश्विक स्थिति नहीं** — प्रत्येक `Document` स्व-निहित और `Send + Sync` है
- **C/C++ FFI** — C/C++ प्रोजेक्ट्स में एम्बेड करने के लिए हेडर फ़ाइल (`include/xmloxide.h`) के साथ पूर्ण C API
- **न्यूनतम निर्भरताएँ** — केवल `encoding_rs` (लाइब्रेरी में शून्य अन्य निर्भरताएँ; `clap` केवल CLI के लिए)
## त्वरित प्रारंभ
```rust
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");
```
### सीरियलाइज़ेशन
```rust
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 क्वेरीज़
```rust
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 स्ट्रीमिंग
```rust
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 पार्सिंग
```rust
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 सेलेक्टर
```rust
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)
```rust
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` के पीछे का एल्गोरिदम) भी समर्थित है:
```rust
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-जैसी)
```rust
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"]);
```
### त्रुटि पुनर्प्राप्ति
```rust
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 टूल
```sh
# पार्स करें और सुंदर-प्रिंट करें
xmllint --format document.xml
# स्कीमा के विरुद्ध सत्यापित करें
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 क्वेरी
xmllint --xpath "//title" document.xml
# कैननिकल XML
xmllint --c14n document.xml
# HTML पार्स करें
xmllint --html page.html
```
## मॉड्यूल अवलोकन
| मॉड्यूल | विवरण |
|--------|-------|
| `tree` | एरेना-आधारित DOM ट्री (`Document`, `NodeId`, `NodeKind`) |
| `parser` | XML 1.0 रिकर्सिव डिसेंट पार्सर त्रुटि पुनर्प्राप्ति के साथ |
| `parser::push` | चंक्ड इनपुट के लिए पुश/इंक्रीमेंटल पार्सर |
| `html` | त्रुटि-सहिष्णु HTML 4.01 पार्सर |
| `html5` | WHATWG HTML लिविंग स्टैंडर्ड पार्सर (टोकनाइज़र + ट्री बिल्डर) |
| `html5::sax` | HTML5 के लिए स्ट्रीमिंग SAX-जैसी API (कोई DOM ट्री नहीं बनता) |
| `css` | दस्तावेज़ ट्री क्वेरी करने के लिए CSS सेलेक्टर इंजन |
| `sax` | SAX2 स्ट्रीमिंग ईवेंट-चालित पार्सर |
| `reader` | XmlReader पुल-आधारित पार्सिंग API |
| `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` | URI रिज़ॉल्यूशन के लिए OASIS XML कैटलॉग |
| `encoding` | कैरेक्टर एन्कोडिंग का पता लगाना और ट्रांसकोडिंग |
| `ffi` | C/C++ FFI बाइंडिंग (`include/xmloxide.h`) |
## प्रदर्शन
libxml2 के मुकाबले पार्सिंग थ्रूपुट प्रतिस्पर्धी है — अधिकांश दस्तावेज़ों पर 3-4% के भीतर, और SVG पर **12% तेज़**। एरेना-आधारित ट्री डिज़ाइन के कारण सीरियलाइज़ेशन **1.5-2.4x तेज़** है। XPath सभी बेंचमार्क में **1.1-2.7x तेज़** है।
**पार्सिंग:**
| दस्तावेज़ | आकार | xmloxide | libxml2 | परिणाम |
|----------|------|----------|---------|--------|
| Atom फ़ीड | 4.9 KB | 26.7 µs (176 MiB/s) | 25.5 µs (184 MiB/s) | ~4% धीमा |
| SVG ड्रॉइंग | 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 पेज | 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 फ़ीड | 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 फ़ास्ट पथ, जीरो-कॉपी एलिमेंट नाम विभाजन, इनलाइन एंटिटी रिज़ॉल्यूशन, XPath `//` स्टेप फ़्यूज़न के साथ फ़्यूज़्ड एक्सिस विस्तार, इनलाइन ट्री एक्सेसर, और चाइल्ड/डिसेंडेंट एक्सिस के लिए नाम परीक्षण फ़ास्ट पथ।
```sh
# बेंचमार्क चलाएं (libxml2 सिस्टम लाइब्रेरी की आवश्यकता)
cargo bench --features bench-libxml2 --bench comparison_bench
```
## परीक्षण
- सभी मॉड्यूल में **1078 यूनिट परीक्षण**
- पूर्ण C API सतह को कवर करने वाले **138 FFI परीक्षण** (SAX, Schematron, और CSS सहित)
- **libxml2 संगतता सूट** — 119/119 परीक्षण पास (100%) XML पार्सिंग, नेमस्पेस, त्रुटि पहचान, और HTML पार्सिंग को कवर करते हुए
- **W3C XML अनुरूपता परीक्षण सूट** — 1727/1727 लागू परीक्षण पास (100%)
- **html5lib-tests** — 7032/7032 टोकनाइज़र परीक्षण + 1778/1778 ट्री निर्माण परीक्षण (100%)
- **एकीकरण परीक्षण** वास्तविक दुनिया के XML/HTML दस्तावेज़, किनारे के मामले और त्रुटि पुनर्प्राप्ति को कवर करते हुए
```sh
cargo test --all-features
```
## C/C++ FFI
xmloxide C/C++ प्रोजेक्ट्स (जैसे क्रोमियम, गेम इंजन, या कोई कोडबेस जो वर्तमान में libxml2 का उपयोग करता है) में एम्बेड करने के लिए C-संगत API प्रदान करता है।
```sh
# शेयर्ड + स्टैटिक लाइब्रेरी बनाएं (शामिल Makefile का उपयोग करता है)
make
# या व्यक्तिगत रूप से बनाएं:
make shared # .so / .dylib / .dll
make static # .a / .lib
# C उदाहरण बनाएं और चलाएं
make example
```
```c
#include "xmloxide.h"
xmloxide_document *doc = xmloxide_parse_str("<root>Hello</root>");
uint32_t root = xmloxide_doc_root_element(doc);
char *name = xmloxide_node_name(doc, root); // "root"
char *text = xmloxide_node_text_content(doc, root); // "Hello"
xmloxide_free_string(name);
xmloxide_free_string(text);
xmloxide_free_doc(doc);
```
पूर्ण API — जिसमें ट्री नेविगेशन और म्यूटेशन, XPath मूल्यांकन, सीरियलाइज़ेशन (सादा और सुंदर-मुद्रित), HTML/HTML5 पार्सिंग, DTD/RelaxNG/XSD/Schematron सत्यापन, C14N, SAX स्ट्रीमिंग, XmlReader, पुश पार्सर और XML कैटलॉग शामिल हैं — को [`include/xmloxide.h`](https://github.com/jonwiggins/xmloxide/blob/main/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(...)` | `xmloxide_create_element` |
| `xmlNewText` | `doc.create_node(Text{..})` | `xmloxide_create_text` |
| `xmlAddChild` | `doc.append_child(p, c)` | `xmloxide_append_child` |
| `xmlAddPrevSibling` | `doc.insert_before(ref, c)` | `xmloxide_insert_before` |
| `xmlUnlinkNode` | `doc.remove_node(id)` | `xmloxide_remove_node` |
| `xmlCopyNode` | `doc.clone_node(id, deep)` | `xmloxide_clone_node` |
| `xmlGetID` | `doc.element_by_id(s)` | `xmloxide_element_by_id` |
| `xmlDocDumpMemory` | `serial::serialize(&doc)` | `xmloxide_serialize` |
| `xmlDocDumpFormatMemory` | `serial::serialize_with_options` | `xmloxide_serialize_pretty` |
| `htmlDocDumpMemory` | `serial::html::serialize_html` | `xmloxide_serialize_html` |
| `xmlC14NDocDumpMemory` | `serial::c14n::canonicalize` | `xmloxide_canonicalize` |
| `xmlXPathEvalExpression` | `xpath::evaluate` | `xmloxide_xpath_eval` |
| `xmlValidateDtd` | `validation::dtd::validate` | `xmloxide_validate_dtd` |
| `xmlRelaxNGValidateDoc` | `validation::relaxng::validate` | `xmloxide_validate_relaxng` |
| `xmlSchemaValidateDoc` | `validation::xsd::validate_xsd` | `xmloxide_validate_xsd` |
| (Schematron सत्यापन) | `validation::schematron::validate_schematron` | `xmloxide_validate_schematron` |
| `xmlXIncludeProcess` | `xinclude::process_xincludes` | `xmloxide_process_xincludes` |
| `xmlLoadCatalog` | `Catalog::parse` | `xmloxide_parse_catalog` |
| `xmlSAX2...` कॉलबैक | `sax::SaxHandler` ट्रेट | `xmloxide_sax_parse` |
| `xmlTextReaderRead` | `reader::XmlReader` | `xmloxide_reader_read` |
| `xmlCreatePushParserCtxt` | `parser::PushParser` | `xmloxide_push_parser_new` |
| `xmlParseChunk` | `PushParser::push` | `xmloxide_push_parser_push` |
**थ्रेड सुरक्षा:** libxml2 के विपरीत, xmloxide में कोई वैश्विक स्थिति नहीं है। प्रत्येक `Document` स्व-निहित और `Send + Sync` है। FFI लेयर अंतिम त्रुटि संदेश के लिए थ्रेड-लोकल स्टोरेज का उपयोग करती है — प्रत्येक थ्रेड की अपनी त्रुटि स्थिति होती है। किसी आरंभीकरण या सफाई फ़ंक्शन की आवश्यकता नहीं है।
## फ़ज़िंग
xmloxide में सुरक्षा परीक्षण के लिए फ़ज़ लक्ष्य शामिल हैं:
```sh
# cargo-fuzz इंस्टॉल करें (नाइटली की आवश्यकता)
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
```
## निर्माण
```sh
cargo build
cargo test
cargo clippy --all-targets --all-features -- -D warnings
cargo bench
```
न्यूनतम समर्थित Rust संस्करण: **1.81**
## सीमाएँ
- **कोई XML 1.1 नहीं** — xmloxide केवल XML 1.0 (पाँचवाँ संस्करण) लागू करता है। XML 1.1 का उपयोग बहुत कम होता है और इसकी योजना नहीं है।
- **कोई XSLT नहीं** — XSLT एक अलग विनिर्देश (libxslt) है और दायरे से बाहर है।
- **HTML पार्सर** — एक HTML 4.01 पार्सर (libxml2 के व्यवहार से मेल खाता है) और एक पूर्ण WHATWG HTML5 पार्सर दोनों प्रदान किए गए हैं। HTML5 पार्सर html5lib-tests का 100% पास करता है।
- **पुश पार्सर आंतरिक रूप से बफ़र करता है** — पुश/इंक्रीमेंटल पार्सर API (`PushParser`) वर्तमान में सभी पुश किए गए डेटा को बफ़र करता है और `finish()` पर पूर्ण पार्स करता है, बजाय libxml2 के `xmlParseChunk` की तरह वास्तविक स्ट्रीमिंग के। SAX स्ट्रीमिंग (`parse_sax` XML के लिए, `html5::sax::parse_html5_sax` HTML5 के लिए) मेमोरी-सीमित बड़े दस्तावेज़ प्रसंस्करण के लिए एक विकल्प के रूप में उपलब्ध है।
- **XPath `namespace::` एक्सिस** — जब इन-स्कोप नेमस्पेस मेल खाते हैं तो `namespace::` एक्सिस एलिमेंट नोड लौटाता है (अलग-अलग नेमस्पेस नोड्स को मटेरियलाइज़ करने के बजाय), गुण एक्सिस के समान पैटर्न का पालन करता है।
## योगदान
विकास सेटअप और दिशानिर्देशों के लिए [CONTRIBUTING.md](https://github.com/jonwiggins/xmloxide/blob/main/CONTRIBUTING.md) देखें।
## चेंजलॉग
संस्करण इतिहास के लिए [CHANGELOG.md](https://github.com/jonwiggins/xmloxide/blob/main/CHANGELOG.md) देखें।
## लाइसेंस
MIT