A pure-Rust implementation of XPath 1.0 — parses an XPath expression and evaluates it against a document.
Generic and standalone — not tied to HTML, XML parsing, Schematron, or any specific document type. Callers provide their own parsed document via a trait; this crate only implements the XPath side (expression parsing, the data model view over the caller's tree, and evaluation).
Implemented: the lexer/parser, the full location-path/predicate evaluation
core (§2), and the complete XPath 1.0 Core Function Library (§4, all 27
functions, including id()). 147 tests pass, clippy and fmt are clean.
use xpath_eval::{Document, EvaluationContext, evaluate, parse};
// A caller brings their own tree, implementing `Node`/`Document` over it —
// this crate never parses or builds one itself. See `Node`'s doc comments
// for what each method must return.
# fn example<D: Document>(doc: &D) -> Result<(), Box<dyn std::error::Error>> {
let expr = parse("//item[@id='42']/name/text()")?;
let ctx = EvaluationContext::new(doc.root());
let result = evaluate(&expr, &ctx)?;
println!("{}", result.to_xpath_string());
# Ok(())
# }EvaluationContext::new covers the common case (no variables, no namespace
context). For XPath variables ($x) or prefixed name tests (p:foo) that
need real namespace resolution, build an EvaluationContext directly and
supply a variables/namespaces lookup hook — see their doc comments.
id()needsNode::is_id_attribute()to be overridden to do anything useful. The XPath 1.0 data model derives ID-ness from a DTD or schema; mostNodeimplementations have no such information, so the trait's default (falseeverywhere) meansid()is a real function that simply never matches. Overrideis_id_attribute()on yourAttribute-kind nodes if you know which attributes are IDs.- Namespace-prefix resolution requires a
namespaceshook. Without one onEvaluationContext, a prefixed name test (p:foo) never matches any node — it does not fall back to guessing. Supply a resolver hook for spec-correct prefix→URI resolution. following/precedingare full document-order axes (§2.3): each walk touches the potentially-large before/after portion of the whole tree, not just nearby nodes. Fine for typical documents; be aware of the cost on very large trees.- Implementing
Nodecorrectly requiresdocument_orderto be a genuine total order consistent withchildren()/attributes()/namespaces()— see the trait's doc comments for the exact ordering rules (element before its namespace nodes before its attribute nodes before its children).
[dependencies]
xpath-eval = "0.2"MIT — see LICENSE.