XML is one of the most frequently split payload types. This recipe will show how you can use Camel's support for implicit type conversion, and XPath support, to process XML fragments individually.
The Java code for this recipe is located in the org.camelcookbook.splitjoin.splitxml
package. The Spring XML files are located under src/main/resources/META-INF/spring
and prefixed with splitXml
.
Consider the following XML file:
<books> <book category="Tech" title="Apache Camel Developer's Cookbook"> <authors> <author>Scott Cranton</author> <author>Jakub Korab</author> </authors> </book> <book category="Cooking" title="Camel Cookbook"> <authors> <author>Heston Ramsey</author> <author>Gordon Blumenthal</author> </authors> </book> </books>
To extract the authors of a particular book category, you use an XPath Expression within the split
DSL statement to isolate the nodes that you are interested in.
In the XML DSL, this is written as:
<from uri="direct:in"/> <split> <xpath>//book[@category='Tech']/authors/author/text()</xpath> <to uri="mock:out"/> </split>
In the Java DSL, the same route is expressed as:
from("direct:in") .split(xpath("//book[@category='Tech']/authors/author/text()")) .to("mock:out") .end();
As a result, two messages will be sent to the mock:out
endpoint:
Scott Cranton
Jakub Korab
The XPath Expression is used to uniquely identify XML DOM nodes that should be isolated for splitting. The Splitter processes each of the identified nodes, and any child nodes, through the steps defined within the split
statement.
The preceding example assumes an XML document without any namespaces. Using XML namespaces with XPath involves only a little bit of extra configuration.
Assume that the document being passed through the route varies from the previous example only in the definition of a namespace:
<books xmlns="http://camelcookbook.org/schema/books">
<!-- remainder as per example above -->
</books>
In order for our XPath Expression to match, we will need to refer to the namespace through a prefix (c:
):
//c:book[@category='Tech']/c:authors/c:author/text()
What remains is to define the association between the full namespace URI and that prefix.
In the XML DSL, the namespace definition is provided on some parent XML element such as the camelContext
:
<camelContext xmlns="http://camel.apache.org/schema/spring" xmlns:c="http://camelcookbook.org/schema/books"> <route> <from uri="direct:in"/> <split> <xpath> //c:book[@category='Tech']/c:authors/c:author/text() </xpath> <to uri="mock:out"/> </split> </route> </camelContext>
When using the Java DSL, you can append a namespace to the xpath()
expression:
from("direct:in")
.split(
xpath(
"//c:book[@category='Tech']/c:authors/c:author/text()"
).namespace("c", "http://camelcookbook.org/schema/books")
)
.to("mock:out")
.end();
Once again, two messages will be sent to the mock:out
endpoint:
Scott Cranton
Jakub Korab
Multiple namespaces can be defined through chaining as follows:
.namespace("c", "http://camelcookbook.org/schema/books") .namespace("se", "http://camelcookbook.org/schema/somethingElse")
If you are going to be using the same namespaces in multiple places throughout your routes, you can make use of the org.apache.camel.builder.xml.Namespaces
builder to avoid repeating that definition:
Namespaces ns = new Namespaces("c", "http://camelcookbook.org/schema/books") .add("se", "http://camelcookbook.org/schema/somethingElse"); from("direct:in") .split( ns.xpath( "//c:book[@category='Tech']/c:authors/c:author/text()" ) ) .to("mock:out") .end();
When splitting via an XPath Expression, Camel loads the entire XML document into memory. This may not be desirable for very large messages. See the Splitter documentation on the Camel website for details on using the Tokenizer Expression Language along with streaming to address this. You can also use the Camel StAX Component for this purpose.
3.145.86.183