Data mapping and transformation with Camunda Spin and Java 8

By
  • Blog
  • >
  • Data mapping and transformation with Camunda Spin and Java 8
TOPICS

30 Day Free Trial

Bring together legacy systems, RPA bots, microservices and more with Camunda

Sign Up for Camunda Content

Get the latest on Camunda features, events, top trends, and more.

TRENDING CONTENT
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

Try All Features of Camunda

Related Content

We're streamlining Camunda product APIs, working towards a single REST API for many components, simplifying the learning curve and making installation easier.
Learn about our approach to migration from Camunda 7 to Camunda 8, and how we can help you achieve it as quickly and effectively as possible.
We've been working hard to reduce the job activation latency in Zeebe. Read on to take a peek under the hood at how we went about it and then verified success.