Apache Camel’s Java DSL lets you plug data formats directly into your routes using fluent marshal() and unmarshal() calls, including both built‑in and custom implementations.
Basics: marshal and unmarshal in Java DSL
In Java DSL, marshal() and unmarshal() are methods on RouteBuilder that apply a DataFormat to the message body.
marshal()converts a Java object (or other in‑memory representation) into a binary or textual format for the “wire”.unmarshal()converts incoming bytes/text into a Java representation, often a POJO, Map, or List.
Example with a built‑in CSV data format:
public class CsvRoute extends RouteBuilder {
@Override
public void configure() {
from("direct:format")
.setBody(constant(Map.of("foo", "abc", "bar", 123)))
.marshal().csv() // Java Map -> CSV line
.to("log:csv");
}
}
The rationale is that your route expresses what transformation should happen (JSON, CSV, XML, etc.) while Camel’s data formats handle how to do it.
Three Java DSL styles for data formats
The Java DSL gives you several ways to use data formats.
1. Passing a DataFormat instance
You create and configure a DataFormat in Java and pass it to marshal()/unmarshal().
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
public class BindyRoute extends RouteBuilder {
@Override
public void configure() {
DataFormat myCsv = new BindyCsvDataFormat(MyModel.class);
from("file:data/in?noop=true")
.unmarshal(myCsv) // CSV -> MyModel instances
.to("bean:processModel");
}
}
Why use this: you get full type‑safe configuration in code and can reuse the same DataFormat instance across routes.
2. Short “dot” helpers: .marshal().json(), .unmarshal().csv()
For common formats, you can use the fluent helpers returned by marshal() and unmarshal().
public class JsonRoute extends RouteBuilder {
@Override
public void configure() {
from("direct:toJson")
.marshal().json() // uses default JSON data format (e.g. Jackson)
.to("log:json");
}
}
This is concise but exposes only basic configuration options; you switch to the other styles if you need more control (like custom delimiters or modules).
3. Data Format DSL: marshal(dataFormat().csv().delimiter(","))
Camel 4 adds a dedicated Data Format DSL accessed via dataFormat(), which you can pass into marshal()/unmarshal().
public class CsvDslRoute extends RouteBuilder {
@Override
public void configure() {
from("direct:format")
.setBody(constant(Map.of("foo", "abc", "bar", 123)))
.marshal(
dataFormat()
.csv() // choose CSV
.delimiter(",") // customize delimiter
.end() // build DataFormat
)
.to("log:csv");
}
}
Rationale: the Data Format DSL is still Java DSL, but it exposes the full configuration surface in a fluent, type‑safe way, without manually constructing the underlying DataFormat.
Custom DataFormat in Java DSL
You can plug any class implementing org.apache.camel.spi.DataFormat directly into the Java DSL. The following example uses the earlier “Hello‑line” format and wires it into routes.
Custom HelloLineDataFormat
import org.apache.camel.Exchange;
import org.apache.camel.spi.DataFormat;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class HelloLineDataFormat implements DataFormat {
@Override
public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
String body = exchange.getContext()
.getTypeConverter()
.mandatoryConvertTo(String.class, graph);
String encoded = "HELLO|" + body + "\n";
stream.write(encoded.getBytes(StandardCharsets.UTF_8));
}
@Override
public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
String text = toString(stream);
if (!text.startsWith("HELLO|")) {
throw new IllegalArgumentException("Invalid format, expected HELLO| prefix: " + text);
}
String withoutPrefix = text.substring("HELLO|".length());
return withoutPrefix.trim();
}
private String toString(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
return out.toString(StandardCharsets.UTF_8);
}
@Override
public void start() {
// No special startup logic needed for this data format
}
@Override
public void stop() {
// No special shutdown logic needed for this data format
}
}
Rationale: the custom format encapsulates both how to decorate the text and how to validate it, so routes never touch the "HELLO|" prefix logic directly.
Using the custom format with Java DSL
Here’s a minimal working example that wires the custom data format into Java DSL routes.
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class HelloLineRouteBuilder extends RouteBuilder {
private final HelloLineDataFormat helloFormat = new HelloLineDataFormat();
@Override
public void configure() {
// Example: marshal plain text to custom wire format
from("timer:hello?period=5000")
.setBody(constant("World"))
.marshal(helloFormat) // "World" -> "HELLO|World\n"
.to("stream:out")
.to("direct:incomingHello"); // Send marshaled data to the direct route
// Example: unmarshal from custom wire format back to String
from("direct:incomingHello")
.unmarshal(helloFormat) // "HELLO|World\n" -> "World"
.log("Decoded body = ${body}");
}
// Small bootstrapper
void main() throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new HelloLineRouteBuilder());
context.start();
Thread.sleep(30_000);
context.stop();
}
}
Why this structure:
- The
HelloLineDataFormatinstance is a normal field, so you can reuse it in multiple routes in the sameRouteBuilder. - The Java DSL remains expressive: you can read the route as “take the timer body, marshal with
helloFormat, send to stdout”, which keeps data‑format details out of the route’s core logic. - The
direct:incomingHelloroute applies.unmarshal(helloFormat)to turn"HELLO|World\n"back into"World", validating the round‑trip symmetry of your customDataFormatin a realistic producer–consumer arrangement.
Choosing an approach in Java DSL
As a Java‑DSL user, you typically choose between:
- Helpers (
.marshal().json(),.unmarshal().csv()): quickest for standard cases. - Explicit instances (
new JaxbDataFormat(...)): best when you need programmatic configuration or dependency‑injected collaborators. - Data Format DSL (
marshal(dataFormat().csv().delimiter(";"))): clean, fluent configuration for complex built‑in formats. - Custom
DataFormatclasses (marshal(new MyCustomFormat())): when the wire format is non‑standard or proprietary.
Leave a Reply