Chapter 32. 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.

32.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]}