Camel Components

DataSonnet

Since Camel 3.7

Camel supports DataSonnet transformations to allow an Expression or Predicate to be used in the DSL.

For example, you could use DataSonnet to create a Predicate in a Message Filter or as an Expression for a Recipient List.

To use a DataSonnet expression, use the following Java code:

Java-only: creating a DataSonnet expression
datasonnet("someDSExpression");

DataSonnet Options

The DataSonnet language supports the following options which are listed below.

Name Default Java Type Description

bodyMediaType (common)

String

The media type of the message body, such as application/json.

outputMediaType (common)

String

The media type to use for the output result.

source (common)

String

Source to use, instead of message body. You can prefix with variable:, header:, or property: to specify kind of source. Otherwise, the source is assumed to be a variable. Use empty or null to use default source, which is the message body.

resultType (common)

String

The class of the result type (type from output).

trim (advanced)

true

Boolean

Whether to trim the source code to remove leading and trailing whitespaces and line breaks.

resolveResource (advanced)

false

Boolean

Whether a result of the expression that is a String starting with resource: is loaded as a resource and its content becomes the result, e.g. a script that returns resource:file:order.json or resource:classpath:templates/order.json (a name without a scheme is a classpath resource). Off by default; the resource: prefix on the expression text itself is always resolved. Applies to the expression used as a value, not as a predicate.

Usage

Setting a result type

The DataSonnet expression will return a com.datasonnet.document.Document by default. The document preserves the content type metadata along with the contents of the transformation result. In predicates, however, the Document will be automatically unwrapped and the boolean content will be returned. Similarly, any time you want the content in a specific result type like a String. To do this, you have to instruct the DataSonnet which result type to return.

  • Java

  • XML

  • YAML

from("direct:start")
    .setBody(datasonnet("body.foo", String.class))
    .to("mock:result");
<route>
  <from uri="direct:start"/>
  <setBody>
    <datasonnet resultType="java.lang.String">body.foo</datasonnet>
  </setBody>
  <to uri="mock:result"/>
</route>
- route:
    from:
      uri: direct:start
      steps:
        - setBody:
            expression:
              datasonnet:
                expression: body.foo
                resultType: java.lang.String
        - to:
            uri: mock:result

In XML and YAML DSL, resultType is the fully qualified class name.

If the expression results in an array, or an object, you can instruct the expression to return you List.class or Map.class, respectively. However, you must also set the output media type to application/x-java-object.

The default Document object is useful in situations where there are intermediate transformation steps, and so retaining the content metadata through a route execution is valuable.

Specifying Media Types

Traditionally, the input and output media types are specified through the DataSonnet Header. The DataSonnet expression provides convenience options for specifying the body and output media types without the need for a Header, this is useful if the transformation is a one-liner, for example.

The DataSonnet expression will look for a body media type in the following order:

  1. If the body is a Document it will use the metadata in the object

  2. If the bodyMediaType parameter was provided in the DSL, it will use its value

  3. A CamelDatasonnetBodyMediaType exchange property

  4. A Content-Type message header

  5. The DataSonnet Header payload media type directive

  6. application/x-java-object

And for output media type:

  1. If the outputMediaType parameter was provided in the DSL, it will use its value

  2. A "CamelDatasonnetOutputMediaType" exchange property

  3. A "CamelDatasonnetOutputMediaType" message header

  4. The DataSonnet Header output media type directive

  5. application/x-java-object

Functions

Camel adds the following DataSonnet functions that can be used to access the exchange:

Function Argument Type Description

cml.properties

key for property

String

To look up a property using the Properties component (property placeholders).

cml.header

the header name

String

Will return the message header.

cml.exchangeProperty

key for property

String

Will return the exchange property.

cml.variable

the variable name

String

Will return the exchange variable.

Null Handling Functions

Function Example Description

cml.defaultVal(value, fallback)

cml.defaultVal(body.currency, 'USD')

Returns value if non-null, fallback otherwise.

cml.isEmpty(value)

cml.isEmpty(body.name)

Returns true if the value is null, an empty string, or an empty array.

Type Coercion Functions

Function Example Description

cml.toInteger(value)

cml.toInteger('42')

Converts a string or number to an integer. Returns null for null input.

cml.toDecimal(value)

cml.toDecimal('3.14')

Converts a string to a decimal number. Returns null for null input.

cml.toBoolean(value)

cml.toBoolean('true')

Converts a string (true/false/yes/no/1/0) or number to boolean. Returns null for null input.

Date/Time Functions

Function Example Description

cml.now()

cml.now()

Returns the current timestamp as an ISO-8601 string.

cml.nowFmt(format)

cml.nowFmt('yyyy-MM-dd')

Returns the current UTC time formatted with the given pattern.

cml.formatDate(value, format)

cml.formatDate('2026-03-20T10:30:00Z', 'dd/MM/yyyy')

Formats an ISO-8601 date, date-time or time string (or epoch milliseconds) using the given pattern, in the default locale. Returns null for null input.

cml.formatDateLocale(value, format, locale)

cml.formatDateLocale('2026-03-20', 'dd MMM yyyy', 'en')

Like cml.formatDate, in the given locale (a language tag such as en or de-DE).

cml.parseDate(value, format)

cml.parseDate('20/03/2026', 'dd/MM/yyyy')

Parses a date string into epoch milliseconds. Returns null for null input.

cml.formatNumber(value, format)

cml.formatNumber(1234.5, ',#0.00')

Formats a number with a java.text.DecimalFormat pattern, in the default locale. Any other value (such as a string) is returned as is.

cml.formatNumberLocale(value, format, locale)

cml.formatNumberLocale(1234.5, ',#0.00', 'de')

Like cml.formatNumber, in the given locale (a language tag such as en or de-DE).

cml.parseDateTime(value, format, type)

cml.parseDateTime('01/31/2020', 'MM/dd/yyyy', 'Date')

Parses a string in the given format (ISO-8601 when the format is null), or a number of seconds since the epoch, into an ISO-8601 string of the given type: Date, DateTime, LocalDateTime, Time or LocalTime. Returns null for null input.

cml.dateAdd(value, amount)

cml.dateAdd('2020-01-31', 'P1D')

Adds an ISO-8601 period or duration (such as P1D, PT2H or -P1M) to an ISO-8601 date or time. Returns null for null input.

cml.datePart(value, part)

cml.datePart('2020-01-31', 'year')

A part of an ISO-8601 date or time: year, month, day, hour, minutes, seconds, milliseconds, nanoseconds, dayOfWeek, dayOfYear, quarter, offsetSeconds or timezone. Returns null for null input.

Math Functions

Function Example Description

cml.sqrt(value)

cml.sqrt(body.x)

Computes the square root of a number. Returns null for null input.

Utility Functions

Function Example Description

cml.uuid()

cml.uuid()

Generates a random UUID string.

cml.typeOf(value)

cml.typeOf(body.x)

Returns the type name: string, number, boolean, object, array, null, or function.

Camel Standard Library (camel.libsonnet)

Camel ships a standard library of helper functions that can be imported in any DataSonnet script:

local c = import 'camel.libsonnet';
{
  total: c.sumBy(body.items, function(i) i.price * i.qty),
  names: c.join(std.map(function(i) i.name, body.items), ', '),
  topItem: c.first(c.sortBy(body.items, function(i) -i.price))
}

String Helpers

Function Description

c.capitalize(s)

Capitalizes the first letter.

c.trim(s)

Strips whitespace from both ends.

c.split(s, sep)

Splits a string by separator.

c.join(arr, sep)

Joins an array of strings with separator.

c.contains(s, sub)

Checks if string contains substring.

c.startsWith(s, prefix)

Checks if string starts with prefix.

c.endsWith(s, suffix)

Checks if string ends with suffix.

c.replace(s, old, new)

Replaces all occurrences.

c.lower(s) / c.upper(s)

Case conversion.

Collection Helpers

Function Description

c.sum(arr)

Sums all elements.

c.sumBy(arr, f)

Sums by applying function f to each element.

c.first(arr) / c.last(arr)

First/last element, or null if empty.

c.count(arr)

Array length.

c.avg(arr)

Average of all elements, or null if empty.

c.distinct(arr)

Removes duplicates.

c.distinctBy(arr, f)

Removes duplicates by key function (keeps first occurrence).

c.flatMap(arr, f)

Maps and flattens.

c.sortBy(arr, f)

Sorts by key function.

c.groupBy(arr, f)

Groups into object by key function.

c.min(arr) / c.max(arr)

Minimum/maximum element.

c.take(arr, n) / c.drop(arr, n)

First/last n elements.

c.zip(a, b)

Zips two arrays into pairs.

Math Helpers

Function Description

c.abs(x)

Absolute value.

c.round(x)

Rounds to nearest integer.

Object Helpers

Function Description

c.pick(obj, keys)

Selects only the listed keys.

c.omit(obj, keys)

Removes the listed keys.

c.merge(a, b)

Merges two objects (b overrides a).

c.keys(obj) / c.values(obj)

Object keys/values as arrays.

c.entries(obj)

Converts to [{key, value}] array.

c.fromEntries(arr)

Converts [{key, value}] array back to object.

Here’s an example showing some of these functions in use:

  • Java

  • XML

  • YAML

from("direct:in")
    .setBody(datasonnet("'hello, ' + cml.properties('toGreet')", String.class))
    .to("mock:camel");
<route>
    <from uri="direct:in"/>
    <setBody>
        <datasonnet resultType="java.lang.String">'hello, ' + cml.properties('toGreet')</datasonnet>
    </setBody>
    <to uri="mock:camel"/>
</route>
- route:
    from:
      uri: direct:in
      steps:
        - setBody:
            expression:
              datasonnet:
                expression: "'hello, ' + cml.properties('toGreet')"
                resultType: java.lang.String
        - to:
            uri: mock:camel

Loading script from external resource

You can externalize the script and have Apache Camel load it from a resource such as "classpath:", "file:", or "http:".
This is done using the following syntax: "resource:scheme:location", e.g., to refer to a file on the classpath you can do:

Java-only: loading a DataSonnet script from an external resource
.setHeader("myHeader").datasonnet("resource:classpath:mydatasonnet.ds");

Examples

Here is a simple example using a DataSonnet expression as a predicate in a Message Filter:

  • Java

  • XML

  • YAML

// let's route if a line item is over $100
from("queue:foo")
    .filter(datasonnet("ds.arrays.firstWith(body.lineItems, function(item) item > 100) != null"))
    .to("queue:bar");
<route>
    <from uri="queue:foo"/>
    <filter>
        <datasonnet>ds.arrays.firstWith(body.lineItems, function(item) item > 100) != null</datasonnet>
        <to uri="queue:bar"/>
    </filter>
</route>
- route:
    from:
      uri: queue:foo
      steps:
        - filter:
            expression:
              datasonnet:
                expression: "ds.arrays.firstWith(body.lineItems, function(item) item > 100) != null"
            steps:
              - to:
                  uri: queue:bar

Here is an example of a simple DataSonnet expression as a transformation EIP. This example will transform an XML body with lineItems into JSON while filtering out lines that are under 100.

  • Java

  • XML

  • YAML

from("queue:foo")
    .transform(datasonnet("ds.filter(body.lineItems, function(item) item > 100)", String.class, "application/xml", "application/json"))
    .to("queue:bar");
<route>
    <from uri="queue:foo"/>
    <filter>
        <datasonnet bodyMediaType="application/xml" outputMediaType="application/json" resultType="java.lang.String" >
            ds.filter(body.lineItems, function(item) item > 100)
        </datasonnet>
        <to uri="queue:bar"/>
    </filter>
</route>
- route:
    from:
      uri: queue:foo
      steps:
        - transform:
            expression:
              datasonnet:
                expression: "ds.filter(body.lineItems, function(item) item > 100)"
                resultType: java.lang.String
                bodyMediaType: application/xml
                outputMediaType: application/json
        - to:
            uri: queue:bar

DataWeave Support

Camel can execute DataWeave (.dwl) scripts directly by auto-detecting and transpiling them to DataSonnet at load time. No extra configuration is needed — just reference a .dwl resource and Camel handles the conversion automatically.

Auto-detection

DataWeave scripts are detected in two ways:

  • By file extension: any resource ending in .dwl (e.g., resource:classpath:transform.dwl)

  • By content: inline expressions starting with the %dw header

Once detected, the script is transpiled to DataSonnet and executed via the datasonnet-mapper runtime. The conversion happens once at route initialization, so there is no runtime overhead.

Example

Given a DataWeave file transform.dwl on the classpath:

%dw 2.0
output application/json
---
{
    orderId: payload.order_id,
    customer: payload.customer.first_name ++ " " ++ payload.customer.last_name,
    total: payload.items map ((item) -> item.price * item.qty)
                         reduce ((item, acc = 0) -> acc + item)
}

You can use it directly in a route:

  • Java

  • XML

  • YAML

from("direct:in")
    .transform(datasonnet("resource:classpath:transform.dwl", String.class))
    .to("mock:result");
<route>
    <from uri="direct:in"/>
    <transform>
        <datasonnet resultType="java.lang.String">resource:classpath:transform.dwl</datasonnet>
    </transform>
    <to uri="mock:result"/>
</route>
- route:
    from:
      uri: direct:in
      steps:
        - transform:
            expression:
              datasonnet:
                expression: "resource:classpath:transform.dwl"
                resultType: java.lang.String
        - to:
            uri: mock:result

CLI Transpiler

You can also convert DataWeave scripts ahead of time using the Camel CLI:

camel transform dataweave -e "payload.name ++ ' ' ++ payload.surname"
camel transform dataweave --input my-transform.dwl --output my-transform.ds
camel transform dataweave --input src/main/resources/dwl/ --output src/main/resources/ds/

How DataWeave is converted

The converted script keeps the DataWeave semantics, which differ from plain Jsonnet in important ways. For example, in DataWeave a missing field, an index out of range or a selector on null gives null, a field selector on an array selects the field of every element (payload.items.price), and most functions accept null. The converted script uses the dataweave.libsonnet library of camel-datasonnet for these semantics (imported as dw), DataSonnet’s ds library, and the cml functions above.

The conversion either produces a script that gives the same result as the DataWeave script, or fails:

  • Invalid DataWeave fails with a DataWeaveConversionException that reports the line and column.

  • A construct that cannot be converted, and any function or variable the converter does not know, makes the route fail to start with a DataWeaveConversionException, instead of running a script that gives a different result. The CLI transpiler emits such constructs as a TODO comment and null, and reports how many need manual review.

Supported Constructs

  • Header: %dw, output (JSON, XML, CSV, Java and text; for JSON the writer properties skipNullOn, indent and encoding), input, import of the dw::core modules, var and fun declarations (also with default parameters, recursive and calling each other), and type declarations (ignored).

  • Selectors: .field, ."quoted field", [index] (negative from the end), [from to to], ["key"], .*field, ..field, .@attribute, field? and [?(condition)].

  • Mule variables and attributes: payload is the message body, vars.name (or vars["name"]) an exchange variable, and attributes.headers, attributes.queryParams and attributes.uriParams message headers. attributes.method, requestPath, requestUri, queryString, statusCode and reasonPhrase are the corresponding Camel HTTP headers.

  • Operators, with the DataWeave precedence: arithmetic, comparison, ~=, and, or, not, ++, --, removing a key with -, default, if/else, unless/else, match (literal, is Type, matches /regex/, if guards and else cases), do blocks and using.

  • Functions in prefix and infix form (upper(x), xs map f), with lambdas (item, index) → …​, the $, ` and `$ shorthand, and references to functions: map, filter, reduce, flatMap, flatten, distinctBy, groupBy, orderBy, mapObject, filterObject, pluck, keysOf, namesOf, valuesOf, entriesOf, zip, unzip, sizeOf, isEmpty, isBlank, sum, avg, min, max, maxBy, minBy, then, to, upper, lower, trim, contains, startsWith, endsWith, splitBy, joinBy, replace …​ with, matches, scan, find, abs, ceil, floor, round, sqrt, pow, mod, isEven, isOdd, isInteger, isDecimal, typeOf, now, uuid, p, log, fail, read and write, and of the dw::core modules camelize, capitalize, dasherize, underscore, pluralize, singularize, leftPad, rightPad, substringBefore, substringAfter (and the other Strings functions), take, drop, splitAt, divideBy, indexOf, countBy, sumBy, every, some, partition, firstWith and mergeWith.

  • Objects: dynamic keys (key): value, conditional fields (key: value) if condition, and object spread { (expression) } of an object or an array of objects.

  • Strings: interpolation ("Hello $(expression)", "Hello $name" and $ of a lambda), and regular expressions /…​/ in replace (also with a function of the matched groups: replace /(\d+)/ with ($[1] ++ "!")), contains, splitBy, matches, scan and find.

  • Type coercion: as String, as Number, as Boolean, as String {format: …​} of numbers and dates, as Date, as DateTime, as LocalDateTime, as Time and as LocalTime (with or without format), date literals such as |2020-01-31|, date arithmetic with periods such as |P1D|, and the parts of a date such as .year. Dates and times are ISO-8601 strings in the converted script; as in DataWeave, a date coerced with a format is written in that format. Numbers and dates are formatted in the default locale (as DataWeave does), unless the coercion has a locale property.

The expected output of the DataWeave scripts in the tests of the conversion is verified with the DataWeave CLI.

These constructs are not converted (the conversion fails): the update operator, try (dw::Runtime), overloaded functions, custom modules, as with a custom type, XML namespaces, XML attributes in the output, and writer properties other than the ones listed above. The XML support is limited: an element with text is read as its text (as in DataWeave), but the attributes of such an element are only kept when they are selected directly (payload.order.@id) or in a lambda (payload.orders.*order map $.@id).

Dependencies

To use scripting languages in your camel routes, you need to add a dependency on camel-datasonnet.

If you use Maven you could just add the following to your pom.xml, substituting the version number for the latest and greatest release (see the download page for the latest versions).

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-datasonnet</artifactId>
  <version>x.x.x</version>
</dependency>