Working with text based data formats such as Xml and JSON is a common requirement when implementing BPMN processes.
Since version 7.2 Camunda provides an optional library which is called Camunda Spin. Spin is a lightweight wrapper library which provides a easy to use API when working with text based data formats such as XML and JSON.
In this post I show how the Camunda Spin can be used for implementing data transformations and mapping in combination with the Java 8 Stream processing API and contrast this to the classical Java 6 / 7 way of doing it.
Example
The example we use is how to transform a list of The Big Bang Theory episodes (provided as Xml) into JSON output. This source was obtained from the TVRage API and looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<show>
<name>The Big Bang Theory</name>
<totalseasons>8</totalseasons>
<episodelist>
<season no="1">
<episode>
<epnum>1</epnum>
<seasonnum>01</seasonnum>
<prodnum>276023</prodnum>
<airdate>2007-09-24</airdate>
<link>https://www.tvrage.com/The_Big_Bang_Theory/episodes/550436</link>
<title>Pilot</title>
</episode>
<episode>
<epnum>2</epnum>
<seasonnum>02</seasonnum>
<prodnum>3T6601</prodnum>
<airdate>2007-10-01</airdate>
<link>https://www.tvrage.com/The_Big_Bang_Theory/episodes/603610</link>
<title>The Big Bran Hypothesis</title>
</episode>
...As Output we want to obtain a JSON list of all episodes which aired after 2012:
[{
"name":"The Shiny Trinket Maneuver",
"air-date":"2012-01-12"
},
{
"name":"The Recombination Hypothesis",
"air-date":"2012-01-19"
}, ... ]Enable Spin in your project
In order to enable Camunda Spin in your project,
1) add it to your maven dependencies:
<dependencymanagement>
<dependencies>
<dependency>
<groupid>org.camunda.bpm</groupid>
<artifactid>camunda-bom</artifactid>
<version>7.3.0-alpha1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencymanagement>
<dependencies>
<dependency>
<groupid>org.camunda.spin</groupid>
<artifactid>camunda-spin-core</artifactid>
</dependency>
<dependency>
<groupid>org.camunda.spin</groupid>
<artifactid>camunda-spin-dataformat-json-jackson</artifactid>
</dependency>
<dependency>
<groupid>org.camunda.spin</groupid>
<artifactid>camunda-spin-dataformat-xml-dom</artifactid>
</dependency>
</dependencies>2) In your Java Code, add the following static import:
import static org.camunda.spin.Spin.*;Parsing the Xml input with Spin
In order to parse the Xml input, we can use Spin’s XML(…) method:
XML(xmlInput)Spin can consume Xml in the form of a string or a java.io.Reader instance.
The Java 6/7 way
We’ll start with the classical Java way of transforming the input.
Using Spin’s xPath function to iterate through all episodes
In order to iterate through all episodes in the document, we can use Spin’s xPath function:
for (SpinXmlElement element : XML(xmlInput).xPath("/Show/Episodelist/Season/episode").elementList()) {
// work with the element
}Getting the production Year as Integer
Since we only want to retain those episodes which aired after 2012, we need to get the production year element for the episode and interpret it as integer:
for (SpinXmlElement element : XML(xmlInput).xPath("/Show/Episodelist/Season/episode").elementList()) {
int productionYear = Integer.parseInt(element.childElement("airdate").textContent().substring(0, 4));
}Creating an empty JSON Document
Next, lets create an empty JSON document into which we can collect the episodes which aired after 2012:
SpinJsonNode resultJson = JSON("[]");Transform the Episodes into JSON
Next we create a JSON object into which we copy the values from the xml source:
SpinJsonNode episodeJson = JSON("{}")
.prop("name", episode.childElement("title").textContent())
.prop("air-date", episode.childElement("airdate").textContent());The complete Java 6/7 source code then looks like this:
SpinJsonNode resultJson = JSON("[]");
for (SpinXmlElement episode : XML(xmlInput).xPath("/Show/Episodelist/Season/episode").elementList()) {
int productionYear = Integer.parseInt(episode.childElement("airdate").textContent().substring(0, 4));
if(productionYear >= 2012) {
SpinJsonNode episodeJson = JSON("{}")
.prop("name", episode.childElement("title").textContent())
.prop("air-date", episode.childElement("airdate").textContent());
resultJson.append(episodeJson);
}
}The Java 8 Way
Java 8 introduces the Stream API and lambdas. This allows us to write a very compact representation for obtaining the same thing:
final SpinJsonNode resultJson = JSON("[]");
XML(xmlInput)
.xPath("/Show/Episodelist/Season/episode").elementList()
.stream()
.filter(e -> Integer.parseInt(e.childElement("airdate").textContent().substring(0, 4)) >= 2012)
.map(e -> JSON("{}")
.prop("name", e.childElement("title").textContent())
.prop("air-date", e.childElement("airdate").textContent()))
.forEach(e -> resultJson.append(e));What is missing?
The above example is not “purely” functional. In the forEach method we collect the generated customers and append them to the resultJson object.
A more functional approach would use a java.util.stream.Collector:
SpinJsonNode resultJson = XML(xmlInput)
.xPath("/Show/Episodelist/Season/episode").elementList()
.stream()
.filter(e -> Integer.parseInt(e.childElement("airdate").textContent().substring(0, 4)) >= 2012)
.map(e -> JSON("{}")
.prop("name", e.childElement("title").textContent())
.prop("air-date", e.childElement("airdate").textContent()))
.collect(asJsonList());The SpinCollectors.asJsonList() method does not exist yet. This would be something the Spin library could provide. In case anybody would use this?

Camunda Developer Community
Join Camunda’s global community of developers sharing code, advice, and meaningful experiences


