...
Info | ||
---|---|---|
| ||
If the message body is stream based, which means the input it receives is submitted to received by Camel as a stream. That means , then you will only be able to read the content of the stream once. So often Oftentimes when you use using XPath as Message Filter or Content Based Router then you need to access the data will be accessed multiple times, and you should . Therefore use Stream Cachingcaching or convert the message body to a |
Code Block |
---|
from("queue:foo"). .filter().xpath("//foo")). .to("queue:bar") |
Code Block |
---|
from("queue:foo"). .choice().xpath("//foo")).to("queue:bar"). .otherwise().to("queue:others"); |
...
You can easily use namespaces with XPath expressions using the Namespaces helper class.
Wiki Markup |
---|
{snippet:id=example|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/XPathWithNamespacesFilterTest.java} |
Variables
Variables in XPath is defined in different namespaces. The default namespace is http://camel.apache.org/schema/spring
.
Namespace URI | Local part | Type | Description |
---|---|---|---|
http://camel.apache.org/xml/in/ |
|
| the The |
http://camel.apache.org/xml/out/ |
|
| the The |
http://camel.apache.org/xml/function/ |
|
| Camel 2.5: Additional functions. |
http://camel.apache.org/xml/variables/environment-variables |
| Object | OS environment variables. |
http://camel.apache.org/xml/variables/system-properties |
| Object | Java System properties. |
http://camel.apache.org/xml/variables/exchange-property |
|
| the The exchange property. |
Camel will resolve variables according to either:
- namespace given
- no namespace given
Namespace
...
Given
If the namespace is given then Camel is instructed exactly what to return. However when resolving either in or out either IN
or OUT
Camel will try to resolve a header with the given local part first, and return it. If the local part has the value value body
then the body is returned instead.
No
...
Namespace Given
If there is no namespace given then Camel resolves only based on the local part. Camel will try to resolve a variable in the following steps:
- from From
variables
that has been set using thevariable(name, value)
fluent builder. - from From
message.in.header
if there is a header with the given key. - from From
exchange.properties
if there is a property with the given key.
Functions
Camel adds the following XPath functions that can be used to access the exchange:
Function | Argument | Type | Description |
---|---|---|---|
| none |
| Will return the in the |
| the header name | Object | Will return the in the |
| none |
| Will return the out the |
| the header name |
| Will return the out the |
| key for property |
| Camel 2.5: To lookup a property using the Properties component (property placeholders). |
| simple expression | Object | Camel 2.5: To evaluate a Simple expression. |
...
Info |
---|
Note: function:properties and function:simple is not supported when the return type is a NodeSet , such as when using with a Splitter EIP. |
Here's an example showing some of these functions in use.
Wiki Markup |
---|
{snippet:id=ex|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/language/XPathFunctionTest.java} |
Wiki Markup |
---|
{snippet:id=ex|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathFunctionsTest.java} |
Using XML
...
Configuration
If you prefer to configure your routes in your Spring XML file then you can use XPath expressions as follows
Code Block | ||
---|---|---|
| ||
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<camelContext id="camel" xmlns="http://activemq.apache.org/camel/schema/spring" xmlns:foo="http://example.com/person">
<route>
<from uri="activemq:MyQueue"/>
<filter>
<xpath>/foo:person[@name='James']</xpath>
<to uri="mqseries:SomeOtherQueue"/>
</filter>
</route>
</camelContext>
</beans>
|
Notice how we can reuse the namespace prefixes, foo
in this case, in the XPath expression for easier namespace based XPath expressions! See also this discussion on the mailinglist about using your own namespaces with xpathXPath.
Setting
...
the Result Type
The XPath expression will return a result type using native XML objects such as org.w3c.dom.NodeList
. But many times you want a result type to be a String
. To do this you have to instruct the XPath which result type to use.
In Java DSL:
Code Block | ||
---|---|---|
| ||
xpath("/foo:person/@id", String.class)
|
In Spring DSL you use the the resultType
attribute to provide a fully qualified classname:
Code Block | ||||
---|---|---|---|---|
| ||||
<xpath resultType="java.lang.String">/foo:person/@id</xpath>
|
In @XPath
:
Available as of Camel 2.1
Code Block | ||
---|---|---|
| ||
@XPath(value = "concat('foo-',//order/name/)", resultType = String.class) String name)
|
Where we use the xpath function XPath function concat
to prefix the order name with foo-
. In this case we have to specify that we want a a String
as result type so the the concat
function works.
Using XPath on Headers
...
Some users may have XML stored in a header. To apply an XPath statement to a header's value you can do this by defining the 'the headerName
' attribute.
In XML DSL:
Wiki Markup |
---|
{snippet:id=e1|lang=xml|url=camel/trunk/components/camel-test-blueprint/src/test/resources/org/apache/camel/test/blueprint/xpath/XPathHeaderNameTest.xml} |
headerName
as the 2nd parameter as shown:Code Block | ||
---|---|---|
| ||
xpath("/invoice/@orderType = 'premium'", "invoiceDetails")
|
...
Here is a simple example using an XPath expression as a predicate in a Message Filter
Wiki Markup |
---|
{snippet:id=example|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/XPathFilterTest.java} |
NamespaceBuilder
as shown in this exampleWiki Markup |
---|
{snippet:id=example|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/XPathWithNamespaceBuilderFilterTest.java} |
choice
construct. The first choice evaulates if the message has a header key key type
that has the value Camel
. The 2nd 2nd
choice
evaluates if the message body has a name tag tag <name>
which values is Kong
.If neither is true the message is routed in the otherwise block:
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathHeaderTest.java} |
Wiki Markup |
---|
{snippet:id=example|lang=xml|url=camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/SpringXPathHeaderTest-context.xml} |
XPath
...
Injection
You can use Bean Integration to invoke a method on a bean and use various languages such as XPath to extract a value from the message and bind it to a method parameter.
The default XPath annotation has SOAP and XML namespaces available. If you want to use your own namespace URIs in an XPath expression you can use your own copy of the XPath annotation to create whatever namespace prefixes you want to use.
Wiki Markup |
---|
{snippet:id=example|lang=java|url=camel/trunk/camel-core/src/test/java/org/apache/camel/component/xslt/MyXPath.java} |
Example:
Code Block | ||
---|---|---|
| ||
public class Foo {
@MessageDriven(uri = "activemq:my.queue")
public void doSomething(@MyXPath("/ns1:foo/ns2:bar/text()") String correlationID, @Body String body) {
// process the inbound message here
}
}
|
Using XPathBuilder
...
Without an Exchange
Available as of Camel 2.3
You can now use the org.apache.camel.builder.XPathBuilder
without the need for an Exchange. This comes handy if you want to use it as a helper to do custom xpath XPath evaluations. It requires that you pass in a CamelContext since a lot of the moving parts inside the the XPathBuilder
requires access to the Camel Type Converter and hence why CamelContext is needed.
For example you can do something like this:
Code Block | ||
---|---|---|
| ||
boolean matches = XPathBuilder.xpath("/foo/bar/@xyz").matches(context, "<foo><bar xyz='cheese'/></foo>"));
|
...
You can also evaluate for example as shown in the following three examples:
Code Block | ||
---|---|---|
| ||
String name = XPathBuilder.xpath("foo/bar").evaluate(context, "<foo><bar>cheese</bar></foo>", String.class); Integer number = XPathBuilder.xpath("foo/bar").evaluate(context, "<foo><bar>123</bar></foo>", Integer.class); Boolean bool = XPathBuilder.xpath("foo/bar").evaluate(context, "<foo><bar>true</bar></foo>", Boolean.class); |
Evaluating with a String result is a common requirement and thus you can do it a bit simpler:
Code Block |
---|
String name = XPathBuilder.xpath("foo/bar").evaluate(context, "<foo><bar>cheese</bar></foo>");
|
...
Available as of Camel 2.3
You need to add camel-saxon
as dependency to your project. Its It's now easier to use Saxon with the the XPathBuilder
which can be done in several ways as shown below.
Where as the latter ones are the easiest ones.
Using a factory
Wiki Markup |
---|
{snippet:id=e1|lang=java|url=camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathTest.java} |
Wiki Markup |
---|
{snippet:id=e2|lang=java|url=camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathTest.java} |
Wiki Markup |
---|
{snippet:id=e3|lang=java|url=camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathTest.java} |
Setting a
...
Custom XPathFactory
...
Using System Property
Available as of Camel 2.3
Camel now supports reading the JVM system property javax.xml.xpath.XPathFactory
that can be used to set a custom custom XPathFactory
to use.
This unit test shows how this can be done to use Saxon instead:
Wiki Markup |
---|
{snippet:id=e4|lang=java|url=camel/trunk/components/camel-saxon/src/test/java/org/apache/camel/builder/saxon/XPathTest.java} |
INFO
level if it uses a non default default XPathFactory
such as:Code Block |
---|
XPathBuilder INFO Using system property javax.xml.xpath.XPathFactory:http://saxon.sf.net/jaxp/xpath/om with value:
net.sf.saxon.xpath.XPathFactoryImpl when creating XPathFactory
|
To use Apache Xerces you can configure the system property:
Code Block |
---|
-Djavax.xml.xpath.XPathFactory=org.apache.xpath.jaxp.XPathFactoryImpl
|
...
Specifying the factory
Code Block | ||||
---|---|---|---|---|
| ||||
<xpath factoryRef="saxonFactory" resultType="java.lang.String">current-dateTime()</xpath>
|
Specifying the object model
Code Block | ||||
---|---|---|---|---|
| ||||
<xpath objectModel="http://saxon.sf.net/jaxp/xpath/om" resultType="java.lang.String">current-dateTime()</xpath>
|
Shortcut
Code Block | ||||
---|---|---|---|---|
| ||||
<xpath saxon="true" resultType="java.lang.String">current-dateTime()</xpath>
|
Namespace
...
Auditing to
...
Aid Debugging
Available as of Camel 2.10
...
Therefore, the utmost we can do is assist you in debugging such issues by adding two new features to the XPath Expression Language and are thus accesible accessible from both predicates and expressions.
Logging the Namespace Context of
...
Your XPath
...
Expression/
...
Predicate
Every time a new XPath expression is created in the internal pool, Camel will log the namespace context of the expression under the org.apache.camel.builder.xml.XPathBuilder
logger. Since Camel represents Namespace Contexts in a hierarchical fashion (parent-child relationships), the entire tree is output in a recursive manner with the following format:
Code Block |
---|
[me: {prefix -> namespace}, {prefix -> namespace}], [parent: [me: {prefix -> namespace}, {prefix -> namespace}], [parent: [me: {prefix -> namespace}]]]
|
Any of these options can be used to activate this logging:
- Enable Enable
TRACE
logging on theorg.apache.camel.builder.xml.XPathBuilder
logger, or some parent logger such asorg.apache.camel
or the root logger. - Enable the
logNamespaces
option as indicated in Auditing Namespaces, in which case the logging will occur on the theINFO
level.
Anchor | ||||
---|---|---|---|---|
|
...
Camel is able to discover and dump all namespaces present on every incoming message before evaluating an XPath expression, providing all the richness of information you need to help you analyse and pinpoint possible namespace issues. To achieve this, it in turn internally uses another specially tailored XPath expression to extract all namespace mappings that appear in the message, displaying the prefix and the full namespace URI(s) for each individual mapping.
...
- The implicit XML namespace (xmlns:xml="http://www.w3.org/XML/1998/namespace") is suppressed from the output because it adds no value.
- Default namespaces are listed under the the
DEFAULT
keyword in the output. - Keep in mind that namespaces can be remapped under different scopes. Think of a top-level 'a' prefix which in inner elements can be assigned a different namespace, or the default namespace changing in inner scopes. For each discovered prefix, all associated URIs are listed.
...
Java DSL:
Code Block | ||||
---|---|---|---|---|
| ||||
XPathBuilder.xpath("/foo:person/@id", String.class).logNamespaces()
|
Spring DSL:
Code Block | ||||
---|---|---|---|---|
| ||||
<xpath logNamespaces="true" resultType="String">/foo:person/@id</xpath>
|
The result of the auditing will be appear at the the INFO
level under the org.apache.camel.builder.xml.XPathBuilder
logger and will look like the following:
Code Block |
---|
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]}
|
Loading
...
Script from
...
External Resource
Available as of Camel 2.11
You can externalize the script and have Camel load it from a resource such as": classpath:
", "file:"
, or "http:"
.
This is done using the following syntax: "resource:scheme:location"
, e.g., eg to refer to a file on the classpath you can do:
Code Block |
---|
.setHeader("myHeader").xpath("resource:classpath:myxpath.txt", String.class)
|
...