Chapter 29. The XPath Language
Abstract
When processing XML messages, the XPath language enables you to select part of a message, by specifying an XPath expression that acts on the message's Document Object Model (DOM). You can also define XPath predicates to test the contents of an element or an attribute.
29.1. Java DSL
Basic expressions
You can use
xpath("Expression")
to evaluate an XPath expression on the current exchange (where the XPath expression is applied to the body of the current In message). The result of the xpath()
expression is an XML node (or node set, if more than one node matches).
For example, to extract the contents of the
/person/name
element from the current In message body and use it to set a header named user
, you could define a route like the following:
from("queue:foo") .setHeader("user", xpath("/person/name/text()")) .to("direct:tie");
Instead of specifying
xpath()
as an argument to setHeader()
, you can use the fluent builder xpath()
command—for example:
from("queue:foo") .setHeader("user").xpath("/person/name/text()") .to("direct:tie");
If you want to convert the result to a specific type, specify the result type as the second argument of
xpath()
. For example, to specify explicitly that the result type is String
:
xpath("/person/name/text()", String.class)
Namespaces
Typically, XML elements belong to a schema, which is identified by a namespace URI. When processing documents like this, it is necessary to associate namespace URIs with prefixes, so that you can identify element names unambiguously in your XPath expressions. Apache Camel provides the helper class,
org.apache.camel.builder.xml.Namespaces
, which enables you to define associations between namespaces and prefixes.
For example, to associate the prefix,
cust
, with the namespace, http://acme.com/customer/record
, and then extract the contents of the element, /cust:person/cust:name
, you could define a route like the following:
import org.apache.camel.builder.xml.Namespaces;
...
Namespaces ns = new Namespaces("cust", "http://acme.com/customer/record");
from("queue:foo")
.setHeader("user", xpath("/cust:person/cust:name/text()", ns))
.to("direct:tie");
Where you make the namespace definitions available to the
xpath()
expression builder by passing the Namespaces
object, ns
, as an additional argument. If you need to define multiple namespaces, use the Namespace.add()
method, as follows:
import org.apache.camel.builder.xml.Namespaces; ... Namespaces ns = new Namespaces("cust", "http://acme.com/customer/record"); ns.add("inv", "http://acme.com/invoice"); ns.add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
If you need to specify the result type and define namespaces, you can use the three-argument form of
xpath()
, as follows:
xpath("/person/name/text()", String.class, ns)
Auditing namespaces
One of the most frequent problems that can occur when using XPath expressions is that there is a mismatch between the namespaces appearing in the incoming messages and the namespaces used in the XPath expression. To help you troubleshoot this kind of problem, the XPath language supports an option to dump all of the namespaces from all of the incoming messages into the system log.
To enable namespace logging at the
INFO
log level, enable the logNamespaces
option in the Java DSL, as follows:
xpath("/foo:person/@id", String.class).logNamespaces()
Alternatively, you could configure your logging system to enable
TRACE
level logging on the org.apache.camel.builder.xml.XPathBuilder
logger.
When namespace logging is enabled, you will see log messages like the following for each processed message:
2012-01-16 13:23:45,878 [stSaxonWithFlag] INFO XPathBuilder - Namespaces discovered in message: {xmlns:a=[http://apache.org/camel], DEFAULT=[http://apache.org/default], xmlns:b=[http://apache.org/camelA, http://apache.org/camelB]}