Contents
- marshal() vs unmarshal()
- JSON with Jackson
- XML with JAXB
- CSV with Bindy
- Chaining Formats (JSON → XML)
- Custom Data Format
marshal() converts a Java object in the exchange body to a serialized format (e.g., POJO → JSON string). unmarshal() converts a serialized format back to a Java object (e.g., JSON string → POJO).
| Direction | DSL Verb | Example |
| POJO → bytes/String | .marshal() | Java object → JSON / XML / CSV |
| bytes/String → POJO | .unmarshal() | JSON / XML / CSV → Java object |
Add camel-jackson-starter. Use .marshal().json() to serialize a POJO to JSON and .unmarshal().json(MyClass.class) to deserialize.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
</dependency>
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
// Simple POJO
record Order(String id, String product, int quantity) {}
@Component
public class JsonRoute extends RouteBuilder {
@Override
public void configure() {
// POJO → JSON string
from("direct:toJson")
.marshal().json()
.log("JSON: ${body}");
// JSON string → POJO
from("direct:fromJson")
.unmarshal().json(Order.class)
.log("Order id: ${body.id}, product: ${body.product}");
// Full round-trip: receive JSON, deserialize, enrich, re-serialize
from("direct:enrichOrder")
.unmarshal().json(Order.class)
.process(exchange -> {
Order o = exchange.getMessage().getBody(Order.class);
exchange.getMessage().setBody(new Order(o.id(), o.product(), o.quantity() * 2));
})
.marshal().json()
.log("Enriched: ${body}");
}
}
By default, .unmarshal().json() without a type produces a Map<String, Object>. Pass the target class to get a typed POJO. For generic collections use unmarshal().json(new TypeReference<List<Order>>(){}).
Add camel-jaxb-starter. Annotate your class with @XmlRootElement and use .marshal().jaxb() / .unmarshal().jaxb().
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jaxb-starter</artifactId>
</dependency>
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
@XmlRootElement(name = "invoice")
public class Invoice {
@XmlElement public String invoiceId;
@XmlElement public String customer;
@XmlElement public double amount;
public Invoice() {}
public Invoice(String invoiceId, String customer, double amount) {
this.invoiceId = invoiceId; this.customer = customer; this.amount = amount;
}
}
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class XmlRoute extends RouteBuilder {
@Override
public void configure() {
// POJO → XML string
from("direct:toXml")
.marshal().jaxb()
.log("XML:\n${body}");
// XML string → POJO
from("direct:fromXml")
.unmarshal().jaxb(Invoice.class.getPackageName())
.log("Invoice id: ${body.invoiceId}");
}
}
The jaxb() method accepts the package name where the ObjectFactory or @XmlRootElement classes live. Pass the class's package name string: Invoice.class.getPackageName().
Camel Bindy maps CSV rows to POJOs using field-level annotations. Add camel-bindy-starter and annotate your model class.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-bindy-starter</artifactId>
</dependency>
import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;
@CsvRecord(separator = ",", skipFirstLine = true) // skip CSV header row
public class ProductRow {
@DataField(pos = 1) public String sku;
@DataField(pos = 2) public String name;
@DataField(pos = 3) public double price;
@DataField(pos = 4) public int stock;
}
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.springframework.stereotype.Component;
@Component
public class CsvRoute extends RouteBuilder {
@Override
public void configure() {
BindyCsvDataFormat csvFormat = new BindyCsvDataFormat(ProductRow.class);
// CSV file → List<ProductRow>
from("file:/tmp/csv-in?noop=true")
.unmarshal(csvFormat)
.split(body()) // split the List into individual rows
.log("Product: ${body.sku} — ${body.name} @ ${body.price}")
.to("direct:processProduct")
.end();
// List<ProductRow> → CSV string
from("direct:exportCsv")
.marshal(csvFormat)
.to("file:/tmp/csv-out?fileName=export.csv");
}
}
After unmarshal(csvFormat) the body is a List<Map<String, ProductRow>>. Use .split(body()) to iterate over each row individually. Bindy also supports fixed-width (@FixedLengthRecord) and key-value-pair formats.
Combine unmarshal and marshal in the same route to translate between formats. The POJO acts as the intermediate canonical model.
// Convert JSON payload → Java POJO → XML response
from("direct:jsonToXml")
.unmarshal().json(Order.class) // JSON → Order POJO
.marshal().jaxb() // Order POJO → XML
.setHeader(Exchange.CONTENT_TYPE, constant("application/xml"))
.log("Converted to XML:\n${body}");
// Convert XML → POJO → JSON
from("direct:xmlToJson")
.unmarshal().jaxb(Invoice.class.getPackageName()) // XML → Invoice POJO
.marshal().json() // Invoice POJO → JSON
.log("Converted to JSON: ${body}");
Implement the DataFormat interface to create a custom format and register it in the Camel registry.
import org.apache.camel.Exchange;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.support.service.ServiceSupport;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class UpperCaseFormat extends ServiceSupport implements DataFormat {
@Override
public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
String body = exchange.getContext().getTypeConverter()
.convertTo(String.class, graph);
stream.write(body.toUpperCase(java.util.Locale.ROOT)
.getBytes(StandardCharsets.UTF_8));
}
@Override
public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
return new String(stream.readAllBytes(), StandardCharsets.UTF_8).toLowerCase();
}
}
// Register and use the custom format
UpperCaseFormat ucf = new UpperCaseFormat();
from("direct:custom")
.marshal(ucf)
.log("Upper: ${body}")
.unmarshal(ucf)
.log("Lower: ${body}");