XPath language
The
xpathlanguage selects values from an XML message with XPath 1.0 expressions such as/order/@idorcount(//item).
Name: xpath · Crate: cameleer-xml (a smaller subset in cameleer-core) · Cargo feature: xml for XPath 1.0 in full, or xpath for the core subset · Apache Camel: XPath
When to use it
- Read a field from an XML document: an id attribute, a customer name, a status.
- Route or filter XML messages (
/order/@status = 'new'). - Split an XML document into one message per element (
//item). - Pull values out of SOAP responses or legacy XML feeds before handing them to a JSON API or an AI agent tool.
Syntax
With cameleer_main::new_main() (and in the container image) the xpath language is the full
XPath 1.0 engine of cameleer-xml. It replaces the subset in cameleer-core, which ignores
namespaces and is only used when cameleer-xml is not registered.
Supported: location paths with all XPath 1.0 axes, predicates, unions |, and/or,
comparisons, arithmetic (+ - * div mod), the XPath 1.0 function library (count, sum,
concat, contains, starts-with, substring-before, translate, normalize-space,
string-length, number, round, name, local-name, ...) plus upper-case,
lower-case, ends-with and matches.
Camel functions and variables:
| syntax | value |
|---|---|
$name | header name, else exchange property, else variable |
in:body(), in:header('name') | the body, a header |
function:properties('key') | a property placeholder value |
function:simple('${...}') | a Simple expression |
$env:NAME, $system:NAME | an environment variable |
$exchangeProperty:NAME | an exchange property |
Namespaces. Name tests match namespace URIs, so a prefixed document needs a namespace
binding (p=uri) or local-name() tests.
Result. Without resultType, a node-set with one node becomes a string (elements are
serialized as XML, attributes and text as their value), a larger node-set becomes an array,
and numbers, strings and booleans keep their type (XPath numbers are doubles, so count(...)
is 2.0). Use resultType: String for the trimmed string value.
Syntax reference, tested
{"body": "<order id=\"42\" status=\"new\"><customer>Ada</customer><item sku=\"A1\" qty=\"2\" price=\"9.50\"/><item sku=\"B2\" qty=\"1\" price=\"20\"/></order>",
"headers": {"region": "eu"}}| expression | result |
|---|---|
/order/@id | 42 |
/order/customer/text() | Ada |
/order/customer | <customer>Ada</customer> |
//item/@sku | ["A1","B2"] |
//item[1] | <item sku="A1" qty="2" price="9.50"/> |
//item[last()]/@sku | B2 |
//item[@price > 10]/@sku | B2 |
//item/following-sibling::item/@sku | B2 |
count(//item) | 2.0 |
sum(//item/@qty) | 3.0 |
//item[@sku='A1']/@qty * //item[@sku='A1']/@price | 19.0 |
concat(/order/customer, '-', /order/@id) | Ada-42 |
translate(/order/customer, 'a', 'A') | AdA |
upper-case(/order/customer) | ADA |
name(/*) | order |
$region | eu |
in:header('region') | eu |
function:simple('${header.region}') | eu |
| expression | result |
|---|---|
/order/@status = 'new' | true |
count(//item) > 2 | false |
//item[@sku='B2'] | true |
//refund | false |
Examples
Route XML orders by status
- route:
id: xpath-route
from:
uri: direct:xpath-route
steps:
- setHeader:
name: orderId
xpath:
expression: /order/@id
resultType: String
- choice:
when:
- xpath: "/order/@status = 'new'"
steps:
- setBody:
simple: "new order ${header.orderId}"
otherwise:
steps:
- setBody:
simple: "ignored order ${header.orderId}"<order id="42" status="new"><customer>Ada</customer></order>new order 42<order id="43" status="shipped"><customer>Bob</customer></order>ignored order 43Namespaces
- route:
id: xpath-ns
from:
uri: direct:xpath-ns
steps:
- setBody:
xpath:
expression: /o:order/o:total
namespace: o=urn:example:orders
resultType: Double<order xmlns="urn:example:orders"><total>12.5</total></order>12.5Split an XML document into items
- route:
id: xpath-split
from:
uri: direct:xpath-split
steps:
- split:
xpath: //item
aggregationStrategy: GroupedBodyAggregationStrategy
steps:
- setBody:
xpath:
expression: /item/@sku
resultType: String<order><item sku="A1"/><item sku="B2"/><item sku="C3"/></order>["A1","B2","C3"]Options
| option | default | description |
|---|---|---|
resultType | (none) | String (trimmed string value), NodeList/nodeset (array), Node (first node), Number/Double, Integer/Long, Boolean. Java class names and XPathConstants names are accepted. |
namespace / namespaces | (none) | Prefix bindings: p=uri,q=uri2 (also ; or newline separated), a JSON object, or a list of {key, value}. Options named namespace.<prefix> also bind a prefix. |
headerName | (none) | Evaluate this header instead of the body. |
propertyName, variableName | (none) | Evaluate this exchange property or variable. |
source | (none) | header:x, exchangeProperty:x, variable:x or body. |
trim | true | Trim the String result. |
saxon | false | true fails: there is no Saxon or XPath 2.0 engine. |
threadSafety, preCompile, logNamespaces, documentType, factoryRef, objectModel | Accepted for compatibility, no effect. |
Rust DSL
b.from("direct:xpath-rust")
.filter(xpath("/order/@status = 'new'"))
.set_body(xpath_as("/order/@id", "String"));With namespaces, use cameleer_xml::language::xpath_ns("/c:order/@id", &[("c", "urn:orders")]).
Common questions
Why does my XPath return nothing for a document with a default namespace?
Name tests match the namespace URI. Bind a prefix (namespace: o=urn:...) and use it in the
expression, or match with *[local-name()='order'].
Why is count(//item) 2.0 and not 2?
XPath numbers are doubles. Use resultType: Integer to get 2.
Does Cameleer support XPath 2.0 or 3.1?
No. The xpath language is XPath 1.0 with a few 2.0 string functions (upper-case,
lower-case, ends-with, matches). For FLWOR and sequences use the
XQuery language.
Related
- XQuery language, xslt component, xj component, validator component
- tokenize language (
xml: truestreams large XML files element by element) - JacksonXML data format
- Apache Camel XPath language
Found a problem?
If something on this page doesn't match what Cameleer does, or you hit a bug while building
with it, please help fix it, whether you are a person or a coding agent:
open an issue that describes what you saw,
then fork the repository, fix it, add or update a test, run cargo test --workspace, and open
a pull request. CONTRIBUTING.md
explains the ground rules.