oxml
🌐 EN , Change language
llms.txt

Migration Guide

Migrating from roxmltree to oxml

Retain 100% safe Rust while unlocking full XPath 1.0, mutable DOM operations, XSD validation, and unified tooling.

Why Upgrade?

roxmltree is a widely respected read-only XML parser in the Rust community known for its strict #![forbid(unsafe_code)] enforcement. However, many projects eventually hit structural limitations:

  1. Read-Only Wall: roxmltree cannot modify nodes, insert children, update attributes, or serialize XML. oxml provides a mutable generational arena DOM.
  2. Missing XPath 1.0: roxmltree requires manual recursive tree-walking functions. oxml includes a complete, compliant XPath 1.0 engine with standard functions and $O(1)$ integer predicate optimization.
  3. No Schema Validation: roxmltree only verifies XML well-formedness. The oxml ecosystem includes xmlschema with 95.2% W3C XSD conformance.
  4. Beyond a Library: oxml ships a CLI binary (oxml-cli), WebAssembly bindings (oxml-wasm), Language Server (oxml-lsp), and AI Model Context Protocol server (oxml-mcp).

Feature Comparison

Capability roxmltree oxml Toolkit Advantage
Safety (#![forbid(unsafe_code)]) Yes Yes Identical guarantee: zero unsafe blocks
DOM Tree Mutation No (Read-only) Yes Generational slot recycling arena
XML Serialization & Formatting No Yes Indentation and empty-element controls
XPath 1.0 Engine No Yes 13 navigational axes + standard functions
W3C XML Schema (XSD) No Yes 95.2% conformance over 35,942 tests
Zero-Copy Streaming Slice borrowing BorrowedEvent Reader::next_borrowed() zero-copy slices
CLI & Shell Integration No Yes oxml-cli pipe processing
AI / MCP Tools No Yes 5 native tools for Claude & Cursor

Code Migration

1. Document Parsing & Node Access

In roxmltree:

use roxmltree::Document;

let text = "<book id='b1'>Dune</book></library>";
let doc = Document::parse(text)?;

// Manual traversal required to find elements
for node in doc.descendants() {
    if node.tag_name().name() == "book" {
        assert_eq!(node.attribute("id"), Some("b1"));
        assert_eq!(node.text(), Some("Dune"));
    }
}

In oxml:

use oxml::parse;

let text = "<book id='b1'>Dune</book></library>";
let doc = parse(text)?;

// Direct traversal or high-level XPath query
let root = doc.root_element().unwrap();
let book = doc.children(root)[0];

assert_eq!(doc.attribute(book, "id"), Some("b1"));
assert_eq!(doc.text(book), "Dune");

// Or with XPath 1.0:
let titles = doc.select("//book[@id='b1']/text()")?;
assert_eq!(titles[0].to_str(&doc), "Dune");

2. Modifying Nodes and Serializing (Impossible in roxmltree)

roxmltree cannot mutate document trees. In oxml:

use oxml::{Document, parse};

let mut doc = parse("")?;
let root = doc.root_element().unwrap();

// Create and append a new book element
let book = doc.create_element("book");
doc.set_attribute(book, "id", "b2");
doc.append_child(root, book);

// Add text content
let text = doc.create_text("Neuromancer");
doc.append_child(book, text);

// Serialize formatted output
let output = doc.serialize();
assert!(output.contains("<book id=\"b2\">Neuromancer</book>"));

3. Zero-Copy Streaming Borrowing

Both libraries support zero-allocation scanning. In oxml, Reader::next_borrowed() yields BorrowedEvent<'a> items that borrow string slices directly from the input buffer:

use oxml::stream::{BorrowedEvent, Reader};

let mut reader = Reader::new("<items><item id='1'>Data</item></items>")?;
while let Some(event) = reader.next_borrowed()? {
    if let BorrowedEvent::StartElement { name, attributes } = event {
        println!("Element: {}", name.local);
    }
}