XPath
An XPath 1.0 expression and predicate language over sxd-document and sxd-xpath. It evaluates //node and /path queries against an Exchange XML body.
ctx.register_language("xpath", Box::new(XPathLanguage::new()))
.expect("xpath not yet registered"); // allow-unwrap
let lang = XPathLanguage::new();
let title_expr = lang
.create_expression("/catalog/book[1]/title")
.expect("valid expression"); // allow-unwrap
let in_stock_pred = lang
.create_predicate("/catalog/book[@in-stock='true']")
.expect("valid predicate"); // allow-unwrap
let title_expr = Arc::new(title_expr);
let in_stock_pred = Arc::new(in_stock_pred);
let route = RouteBuilder::from("timer:tick?period=800&repeatCount=6")
.route_id("language-xpath-demo")
.process(move |mut exchange: camel_api::Exchange| {
let c = Arc::clone(&counter_clone);
Box::pin(async move {
let n = c.fetch_add(1, Ordering::SeqCst) as usize;
let (title, in_stock) = books[n % books.len()];
let xml = format!(
"<catalog><book id=\"{}\" in-stock=\"{}\"><title>{}</title></book></catalog>",
n + 1,
in_stock,
title
);
exchange.input.body = Body::Xml(xml);
Ok(exchange)
})
})
.process({
let expr = Arc::clone(&title_expr);
move |mut exchange: camel_api::Exchange| {
let expr = Arc::clone(&expr);
Box::pin(async move {
if let Ok(value) = expr.evaluate(&exchange).await
&& let Some(name) = value.as_str()
{
exchange
.input
.set_header("book-title", camel_api::Value::String(name.to_string()));
}
Ok(exchange)
})
}
})
.to("log:all-books?showBody=true&showHeaders=true")
.filter({
let pred = Arc::clone(&in_stock_pred);
let handle = tokio::runtime::Handle::current();
move |ex: &camel_api::Exchange| handle.block_on(pred.matches(ex)).unwrap_or(false)
})
.to("log:in-stock?showBody=true&showHeaders=true")
.end_filter()
.build()?;
YAML equivalent
- id: language-xpath-demo
from: timer:tick?period=800&repeatCount=6
steps:
- set_body:
value: "<catalog><book in-stock='true'><title>The Rust Book</title></book></catalog>"
- set_header:
key: title
xpath: "/catalog/book[1]/title"
- to: log:all-books?showBody=true&showHeaders=true
- filter:
xpath: "/catalog/book[@in-stock='true']"
steps:
- to: log:in-stock?showBody=true&showHeaders=true
You register xpath into CamelContext by name, then build expressions and predicates up front. The included example constructs one expression (/catalog/book[1]/title) and one predicate (/catalog/book[@in-stock='true']) before the route. Expressions extract values from the XML body. Predicates gate filter steps.
The XPath query is trusted operator configuration. The XML body is untrusted, adversary-controlled data under ADR-0032. Exchange data never enters the query string. max_input_bytes bounds the raw XML body before parsing. The default is 1 MiB. Both sxd-document and sxd-xpath are pure Rust and register no filesystem or network resolver. The parser has no <!ENTITY> declaration handler. Recursive entity expansion, including a billion-laughs payload, is structurally unavailable. External entity declarations cannot trigger a file or network fetch.
Known limitations apply. Namespace prefixes are unsupported because the evaluation context has no prefix-to-URI map. Evaluation has no wall-clock timeout. The query is trusted, and the untrusted XML input has a byte bound. The sxd-xpath library is unmaintained. A replacement must preserve the security posture above.
Use XPath when the body carries XML and you need to select elements or test attributes. For structured JSON bodies, use JSONPath.
Reference: Language SPI · XPath crate