{"id":2104,"date":"2026-02-06T16:13:18","date_gmt":"2026-02-06T03:13:18","guid":{"rendered":"https:\/\/www.ronella.xyz\/?p=2104"},"modified":"2026-02-06T16:13:18","modified_gmt":"2026-02-06T03:13:18","slug":"using-apache-camel-data-formats-in-the-java-dsl","status":"publish","type":"post","link":"https:\/\/www.ronella.xyz\/?p=2104","title":{"rendered":"Using Apache Camel Data Formats in the Java DSL"},"content":{"rendered":"<p>Apache Camel\u2019s Java DSL lets you plug data formats directly into your routes using fluent <code>marshal()<\/code> and <code>unmarshal()<\/code> calls, including both built\u2011in and custom implementations.<\/p>\n<h2>Basics: marshal and unmarshal in Java DSL<\/h2>\n<p>In Java DSL, <code>marshal()<\/code> and <code>unmarshal()<\/code> are methods on <code>RouteBuilder<\/code> that apply a <code>DataFormat<\/code> to the message body.<\/p>\n<ul>\n<li><code>marshal()<\/code> converts a Java object (or other in\u2011memory representation) into a binary or textual format for the \u201cwire\u201d.<\/li>\n<li><code>unmarshal()<\/code> converts incoming bytes\/text into a Java representation, often a POJO, Map, or List.<\/li>\n<\/ul>\n<p>Example with a built\u2011in CSV data format:<\/p>\n<pre><code class=\"language-java\">public class CsvRoute extends RouteBuilder {\n    @Override\n    public void configure() {\n        from(&quot;direct:format&quot;)\n            .setBody(constant(Map.of(&quot;foo&quot;, &quot;abc&quot;, &quot;bar&quot;, 123)))\n            .marshal().csv()        \/\/ Java Map -&gt; CSV line\n            .to(&quot;log:csv&quot;);\n    }\n}<\/code><\/pre>\n<p>The rationale is that your route expresses <em>what<\/em> transformation should happen (JSON, CSV, XML, etc.) while Camel\u2019s data formats handle <em>how<\/em> to do it.<\/p>\n<h2>Three Java DSL styles for data formats<\/h2>\n<p>The Java DSL gives you several ways to use data formats.<\/p>\n<h2>1. Passing a <code>DataFormat<\/code> instance<\/h2>\n<p>You create and configure a <code>DataFormat<\/code> in Java and pass it to <code>marshal()<\/code>\/<code>unmarshal()<\/code>.<\/p>\n<pre><code class=\"language-java\">import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;\n\npublic class BindyRoute extends RouteBuilder {\n    @Override\n    public void configure() {\n        DataFormat myCsv = new BindyCsvDataFormat(MyModel.class);\n\n        from(&quot;file:data\/in?noop=true&quot;)\n            .unmarshal(myCsv)      \/\/ CSV -&gt; MyModel instances\n            .to(&quot;bean:processModel&quot;);\n    }\n}<\/code><\/pre>\n<p>Why use this: you get full type\u2011safe configuration in code and can reuse the same <code>DataFormat<\/code> instance across routes.<\/p>\n<h2>2. Short \u201cdot\u201d helpers: <code>.marshal().json()<\/code>, <code>.unmarshal().csv()<\/code><\/h2>\n<p>For common formats, you can use the fluent helpers returned by <code>marshal()<\/code> and <code>unmarshal()<\/code>.<\/p>\n<pre><code class=\"language-java\">public class JsonRoute extends RouteBuilder {\n    @Override\n    public void configure() {\n        from(&quot;direct:toJson&quot;)\n            .marshal().json()      \/\/ uses default JSON data format (e.g. Jackson)\n            .to(&quot;log:json&quot;);\n    }\n}<\/code><\/pre>\n<p>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).<\/p>\n<h2>3. Data Format DSL: <code>marshal(dataFormat().csv().delimiter(&quot;,&quot;))<\/code><\/h2>\n<p>Camel 4 adds a dedicated Data Format DSL accessed via <code>dataFormat()<\/code>, which you can pass into <code>marshal()<\/code>\/<code>unmarshal()<\/code>.<\/p>\n<pre><code class=\"language-java\">public class CsvDslRoute extends RouteBuilder {\n    @Override\n    public void configure() {\n\n        from(&quot;direct:format&quot;)\n            .setBody(constant(Map.of(&quot;foo&quot;, &quot;abc&quot;, &quot;bar&quot;, 123)))\n            .marshal(\n                dataFormat()\n                    .csv()          \/\/ choose CSV\n                    .delimiter(&quot;,&quot;) \/\/ customize delimiter\n                    .end()          \/\/ build DataFormat\n            )\n            .to(&quot;log:csv&quot;);\n    }\n}<\/code><\/pre>\n<p>Rationale: the Data Format DSL is still Java DSL, but it exposes the full configuration surface in a fluent, type\u2011safe way, without manually constructing the underlying <code>DataFormat<\/code>.<\/p>\n<h2>Custom DataFormat in Java DSL<\/h2>\n<p>You can plug any class implementing <code>org.apache.camel.spi.DataFormat<\/code> directly into the Java DSL. The following example uses the earlier \u201cHello\u2011line\u201d format and wires it into routes.<\/p>\n<h2>Custom <code>HelloLineDataFormat<\/code><\/h2>\n<pre><code class=\"language-java\">import org.apache.camel.Exchange;\nimport org.apache.camel.spi.DataFormat;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\n\npublic class HelloLineDataFormat implements DataFormat {\n\n    @Override\n    public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {\n        String body = exchange.getContext()\n                .getTypeConverter()\n                .mandatoryConvertTo(String.class, graph);\n\n        String encoded = &quot;HELLO|&quot; + body + &quot;\\n&quot;;\n        stream.write(encoded.getBytes(StandardCharsets.UTF_8));\n    }\n\n    @Override\n    public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {\n        String text = toString(stream);\n        if (!text.startsWith(&quot;HELLO|&quot;)) {\n            throw new IllegalArgumentException(&quot;Invalid format, expected HELLO| prefix: &quot; + text);\n        }\n        String withoutPrefix = text.substring(&quot;HELLO|&quot;.length());\n        return withoutPrefix.trim();\n    }\n\n    private String toString(InputStream in) throws IOException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        byte[] buf = new byte[1024];\n        int len;\n        while ((len = in.read(buf)) != -1) {\n            out.write(buf, 0, len);\n        }\n        return out.toString(StandardCharsets.UTF_8);\n    }\n\n    @Override\n    public void start() {\n        \/\/ No special startup logic needed for this data format\n    }\n\n    @Override\n    public void stop() {\n        \/\/ No special shutdown logic needed for this data format\n    }\n}<\/code><\/pre>\n<p>Rationale: the custom format encapsulates both how to decorate the text and how to validate it, so routes never touch the <code>&quot;HELLO|&quot;<\/code> prefix logic directly.<\/p>\n<h2>Using the custom format with Java DSL<\/h2>\n<p>Here\u2019s a minimal working example that wires the custom data format into Java DSL routes.<\/p>\n<pre><code class=\"language-java\">import org.apache.camel.CamelContext;\nimport org.apache.camel.builder.RouteBuilder;\nimport org.apache.camel.impl.DefaultCamelContext;\n\npublic class HelloLineRouteBuilder extends RouteBuilder {\n\n    private final HelloLineDataFormat helloFormat = new HelloLineDataFormat();\n\n    @Override\n    public void configure() {\n\n        \/\/ Example: marshal plain text to custom wire format\n        from(&quot;timer:hello?period=5000&quot;)\n            .setBody(constant(&quot;World&quot;))\n            .marshal(helloFormat)           \/\/ &quot;World&quot; -&gt; &quot;HELLO|World\\n&quot;\n            .to(&quot;stream:out&quot;)\n            .to(&quot;direct:incomingHello&quot;);    \/\/ Send marshaled data to the direct route\n\n        \/\/ Example: unmarshal from custom wire format back to String\n        from(&quot;direct:incomingHello&quot;)\n            .unmarshal(helloFormat)         \/\/ &quot;HELLO|World\\n&quot; -&gt; &quot;World&quot;\n            .log(&quot;Decoded body = ${body}&quot;);\n    }\n\n    \/\/ Small bootstrapper\n    void main() throws Exception {\n        CamelContext context = new DefaultCamelContext();\n        context.addRoutes(new HelloLineRouteBuilder());\n        context.start();\n        Thread.sleep(30_000);\n        context.stop();\n    }\n}<\/code><\/pre>\n<p>Why this structure:<\/p>\n<ul>\n<li>The <code>HelloLineDataFormat<\/code> instance is a normal field, so you can reuse it in multiple routes in the same <code>RouteBuilder<\/code>.<\/li>\n<li>The Java DSL remains expressive: you can read the route as \u201ctake the timer body, marshal with <code>helloFormat<\/code>, send to stdout\u201d, which keeps data\u2011format details out of the route\u2019s core logic.<\/li>\n<li>The <code>direct:incomingHello<\/code> route applies <code>.unmarshal(helloFormat)<\/code> to turn <code>&quot;HELLO|World\\n&quot;<\/code> back into <code>&quot;World&quot;<\/code>, validating the <strong>round\u2011trip symmetry<\/strong> of your custom <code>DataFormat<\/code> in a realistic producer\u2013consumer arrangement.<\/li>\n<\/ul>\n<h2>Choosing an approach in Java DSL<\/h2>\n<p>As a Java\u2011DSL user, you typically choose between:<\/p>\n<ul>\n<li><em>Helpers<\/em> (<code>.marshal().json()<\/code>, <code>.unmarshal().csv()<\/code>): quickest for standard cases.<\/li>\n<li><em>Explicit instances<\/em> (<code>new JaxbDataFormat(...)<\/code>): best when you need programmatic configuration or dependency\u2011injected collaborators.<\/li>\n<li><em>Data Format DSL<\/em> (<code>marshal(dataFormat().csv().delimiter(&quot;;&quot;))<\/code>): clean, fluent configuration for complex built\u2011in formats.<\/li>\n<li><em>Custom <code>DataFormat<\/code> classes<\/em> (<code>marshal(new MyCustomFormat())<\/code>): when the wire format is non\u2011standard or proprietary.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Apache Camel\u2019s Java DSL lets you plug data formats directly into your routes using fluent marshal() and unmarshal() calls, including both built\u2011in 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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[92],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2104"}],"collection":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2104"}],"version-history":[{"count":1,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2104\/revisions"}],"predecessor-version":[{"id":2105,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2104\/revisions\/2105"}],"wp:attachment":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2104"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2104"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2104"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}