
How to Implement Custom Expression Language in Camel 3.x
by
Eugene Berman
In this blog post, I’m going to explain the process of implementing a custom expression language support in detail as well as highlight some specifics of the DataSonnet implementation. But first, let me explain how this got started.
Prelude to a post
Shortly after we released the first version of DataSonnet, our open source data transformation platform, a colleague of mine working on the next version of the PortX platform sent me this Slack message:
“Hey, can I use DataSonnet as an expression language in Apache Camel?”
And, I keyed:
“No”
But just as my thumb was reaching for ‘Send’, intuition (and curiosity) arrested it. “Well, hmm. Maybe.” So I backspaced over No, responded to my coworker instead with a boiler-plate, time-buying equivocation, opened DuckDuckGo, and searched for…
“how to implement a custom expression language in Camel”
I was expecting a bunch of results with links to tutorials. To my surprise, nothing useful. It was the same with my Google search, nada. At this point, I realized I would need to do a private investigation and get some help from my colleagues as well.
The Solution
So, after a few days of bouncing ideas off coworkers and playing around with a couple of different approaches, we found a solution. Without further adieu, here’s what we came up with.
Create an empty Maven project
Add the following snippet to the <build><plugins> section:
<plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-package-maven-plugin</artifactId> <version>${camel.version}</version> <executions> <execution> <id>generate</id> <phase>process-classes</phase> <goals> <goal>generate-languages-list</goal> </goals> </execution> </executions> <configuration> <failFast>false</failFast> </configuration> </plugin>
This snippet enables the Camel package plugin.
Next, add the camel-core dependency:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>${camel.version}</version> </dependency>
The ${camel.version} is the version of the Camel core. I tested this solution with Camel 3.2 but it should work with any 3.x version.
Add other dependencies
Finally, add any dependencies that your custom expression language may require. In my case, it included dependencies for the DataSonnet mapper as well as some additional helper libraries.
Create resources
Now it’s time to create some resources.
Create the src/main/resources/META-INF/services/org/apache/camel/language folder and create the file named datasonnet there. The content of this file is:
class=com.modus.camel.datasonnet.language.DatasonnetLanguage
Next, create the file language.properties in the src/main/resources/META-INF/services/org/apache/camel folder:
And finally, create a directory:
src/main/resources/org/apache/camel/language/datasonnet and then create a file datasonnet.json:
Create a Java class
First, create a class for our language:
Create an interface
The interface is used for bean injection:
Create classes
This class represents the DataSonnet expression at the runtime:
Note that this implements the GeneratedPropertyConfigurer interface – this is done so that additional parameters controlling the DataSonnet behavior can be passed via exchange properties. You can see the full implementation of this class at https://github.com/modusbox/camel-datasonnet/blob/master/src/main/java/com/modus/camel/datasonnet/language/DatasonnetExpression.java
In addition to this, a model class extending the ExpressionDefinition should be created:
I also wanted to provide convenient datasonnet() functions to be used in route definitions, so I created an abstract DatasonnetRouteBuilder class:
Please visit the camel-datasonnet GitHub repository for complete code and examples of usage.
Why DataSonnet?
When my coworker originally asked if this was possible, I simultaneously thought to myself, “Why are they asking?” Camel already has many expression languages supported out of the box, including its own Simple language, Groovy, MVEL, and many others. So, why DataSonnet?
For us, the answer is consistency when handling data of different types.
Normally, one would likely use JsonPath to access the JSON formatted data, XPath for XML, and custom Java beans for CSV. And, if you use Javascript, Groovy, or another scripting language, the code gets cluttered with handling the details of the data format. With DataSonnet, you can use the same language when handling all of these formats and create your own plugins if you need to support other formats.
Usage Example: Dynamic queries
Shortly after finishing the first iteration of this project, I was working on an API that returns a number of results from a database based on the request query parameters. All parameters are optional; for example, a consumer should be able to search by email address, first name only, first and last name, or last name only. So the SQL query must be created dynamically. With Datasonnet expression support, this task was simplified. Here’s how I implemented it:
<setBody> <language language=”datasonnet”> local headerNames = ["firstName", "lastName", "email"]; local filteredHeaders = std.foldl( function(aggregate, x) if (std.objectHas(headers, x) > 0) then aggregate + [ x + "' = '" + headers[x] + "'"] else aggregate + [], headerNames, []); "SELECT * from members WHERE " + std.join(" AND ", filteredHeaders) </language> </setBody> <to uri="jdbc:MembersDB"/>
Please check out the DataSonnet project, provide feedback, join us in evolving the tool, and participate with our team in the community.
Learn more about DataSonnet:
Let us know what you think about it in the comments below!
In this blog post, I’m going to explain the process of implementing a custom expression language support in detail as well as highlight some specifics of the DataSonnet implementation. But first, let me explain how this got started.
Prelude to a post
Shortly after we released the first version of DataSonnet, our open source data transformation platform, a colleague of mine working on the next version of the PortX platform sent me this Slack message:
“Hey, can I use DataSonnet as an expression language in Apache Camel?”
And, I keyed:
“No”
But just as my thumb was reaching for ‘Send’, intuition (and curiosity) arrested it. “Well, hmm. Maybe.” So I backspaced over No, responded to my coworker instead with a boiler-plate, time-buying equivocation, opened DuckDuckGo, and searched for…
“how to implement a custom expression language in Camel”
I was expecting a bunch of results with links to tutorials. To my surprise, nothing useful. It was the same with my Google search, nada. At this point, I realized I would need to do a private investigation and get some help from my colleagues as well.
The Solution
So, after a few days of bouncing ideas off coworkers and playing around with a couple of different approaches, we found a solution. Without further adieu, here’s what we came up with.
Create an empty Maven project
Add the following snippet to the <build><plugins> section:
<plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-package-maven-plugin</artifactId> <version>${camel.version}</version> <executions> <execution> <id>generate</id> <phase>process-classes</phase> <goals> <goal>generate-languages-list</goal> </goals> </execution> </executions> <configuration> <failFast>false</failFast> </configuration> </plugin>
This snippet enables the Camel package plugin.
Next, add the camel-core dependency:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>${camel.version}</version> </dependency>
The ${camel.version} is the version of the Camel core. I tested this solution with Camel 3.2 but it should work with any 3.x version.
Add other dependencies
Finally, add any dependencies that your custom expression language may require. In my case, it included dependencies for the DataSonnet mapper as well as some additional helper libraries.
Create resources
Now it’s time to create some resources.
Create the src/main/resources/META-INF/services/org/apache/camel/language folder and create the file named datasonnet there. The content of this file is:
class=com.modus.camel.datasonnet.language.DatasonnetLanguage
Next, create the file language.properties in the src/main/resources/META-INF/services/org/apache/camel folder:
And finally, create a directory:
src/main/resources/org/apache/camel/language/datasonnet and then create a file datasonnet.json:
Create a Java class
First, create a class for our language:
Create an interface
The interface is used for bean injection:
Create classes
This class represents the DataSonnet expression at the runtime:
Note that this implements the GeneratedPropertyConfigurer interface – this is done so that additional parameters controlling the DataSonnet behavior can be passed via exchange properties. You can see the full implementation of this class at https://github.com/modusbox/camel-datasonnet/blob/master/src/main/java/com/modus/camel/datasonnet/language/DatasonnetExpression.java
In addition to this, a model class extending the ExpressionDefinition should be created:
I also wanted to provide convenient datasonnet() functions to be used in route definitions, so I created an abstract DatasonnetRouteBuilder class:
Please visit the camel-datasonnet GitHub repository for complete code and examples of usage.
Why DataSonnet?
When my coworker originally asked if this was possible, I simultaneously thought to myself, “Why are they asking?” Camel already has many expression languages supported out of the box, including its own Simple language, Groovy, MVEL, and many others. So, why DataSonnet?
For us, the answer is consistency when handling data of different types.
Normally, one would likely use JsonPath to access the JSON formatted data, XPath for XML, and custom Java beans for CSV. And, if you use Javascript, Groovy, or another scripting language, the code gets cluttered with handling the details of the data format. With DataSonnet, you can use the same language when handling all of these formats and create your own plugins if you need to support other formats.
Usage Example: Dynamic queries
Shortly after finishing the first iteration of this project, I was working on an API that returns a number of results from a database based on the request query parameters. All parameters are optional; for example, a consumer should be able to search by email address, first name only, first and last name, or last name only. So the SQL query must be created dynamically. With Datasonnet expression support, this task was simplified. Here’s how I implemented it:
<setBody> <language language=”datasonnet”> local headerNames = ["firstName", "lastName", "email"]; local filteredHeaders = std.foldl( function(aggregate, x) if (std.objectHas(headers, x) > 0) then aggregate + [ x + "' = '" + headers[x] + "'"] else aggregate + [], headerNames, []); "SELECT * from members WHERE " + std.join(" AND ", filteredHeaders) </language> </setBody> <to uri="jdbc:MembersDB"/>
Please check out the DataSonnet project, provide feedback, join us in evolving the tool, and participate with our team in the community.
Learn more about DataSonnet:
Let us know what you think about it in the comments below!
In this blog post, I’m going to explain the process of implementing a custom expression language support in detail as well as highlight some specifics of the DataSonnet implementation. But first, let me explain how this got started.
Prelude to a post
Shortly after we released the first version of DataSonnet, our open source data transformation platform, a colleague of mine working on the next version of the PortX platform sent me this Slack message:
“Hey, can I use DataSonnet as an expression language in Apache Camel?”
And, I keyed:
“No”
But just as my thumb was reaching for ‘Send’, intuition (and curiosity) arrested it. “Well, hmm. Maybe.” So I backspaced over No, responded to my coworker instead with a boiler-plate, time-buying equivocation, opened DuckDuckGo, and searched for…
“how to implement a custom expression language in Camel”
I was expecting a bunch of results with links to tutorials. To my surprise, nothing useful. It was the same with my Google search, nada. At this point, I realized I would need to do a private investigation and get some help from my colleagues as well.
The Solution
So, after a few days of bouncing ideas off coworkers and playing around with a couple of different approaches, we found a solution. Without further adieu, here’s what we came up with.
Create an empty Maven project
Add the following snippet to the <build><plugins> section:
<plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-package-maven-plugin</artifactId> <version>${camel.version}</version> <executions> <execution> <id>generate</id> <phase>process-classes</phase> <goals> <goal>generate-languages-list</goal> </goals> </execution> </executions> <configuration> <failFast>false</failFast> </configuration> </plugin>
This snippet enables the Camel package plugin.
Next, add the camel-core dependency:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>${camel.version}</version> </dependency>
The ${camel.version} is the version of the Camel core. I tested this solution with Camel 3.2 but it should work with any 3.x version.
Add other dependencies
Finally, add any dependencies that your custom expression language may require. In my case, it included dependencies for the DataSonnet mapper as well as some additional helper libraries.
Create resources
Now it’s time to create some resources.
Create the src/main/resources/META-INF/services/org/apache/camel/language folder and create the file named datasonnet there. The content of this file is:
class=com.modus.camel.datasonnet.language.DatasonnetLanguage
Next, create the file language.properties in the src/main/resources/META-INF/services/org/apache/camel folder:
And finally, create a directory:
src/main/resources/org/apache/camel/language/datasonnet and then create a file datasonnet.json:
Create a Java class
First, create a class for our language:
Create an interface
The interface is used for bean injection:
Create classes
This class represents the DataSonnet expression at the runtime:
Note that this implements the GeneratedPropertyConfigurer interface – this is done so that additional parameters controlling the DataSonnet behavior can be passed via exchange properties. You can see the full implementation of this class at https://github.com/modusbox/camel-datasonnet/blob/master/src/main/java/com/modus/camel/datasonnet/language/DatasonnetExpression.java
In addition to this, a model class extending the ExpressionDefinition should be created:
I also wanted to provide convenient datasonnet() functions to be used in route definitions, so I created an abstract DatasonnetRouteBuilder class:
Please visit the camel-datasonnet GitHub repository for complete code and examples of usage.
Why DataSonnet?
When my coworker originally asked if this was possible, I simultaneously thought to myself, “Why are they asking?” Camel already has many expression languages supported out of the box, including its own Simple language, Groovy, MVEL, and many others. So, why DataSonnet?
For us, the answer is consistency when handling data of different types.
Normally, one would likely use JsonPath to access the JSON formatted data, XPath for XML, and custom Java beans for CSV. And, if you use Javascript, Groovy, or another scripting language, the code gets cluttered with handling the details of the data format. With DataSonnet, you can use the same language when handling all of these formats and create your own plugins if you need to support other formats.
Usage Example: Dynamic queries
Shortly after finishing the first iteration of this project, I was working on an API that returns a number of results from a database based on the request query parameters. All parameters are optional; for example, a consumer should be able to search by email address, first name only, first and last name, or last name only. So the SQL query must be created dynamically. With Datasonnet expression support, this task was simplified. Here’s how I implemented it:
<setBody> <language language=”datasonnet”> local headerNames = ["firstName", "lastName", "email"]; local filteredHeaders = std.foldl( function(aggregate, x) if (std.objectHas(headers, x) > 0) then aggregate + [ x + "' = '" + headers[x] + "'"] else aggregate + [], headerNames, []); "SELECT * from members WHERE " + std.join(" AND ", filteredHeaders) </language> </setBody> <to uri="jdbc:MembersDB"/>
Please check out the DataSonnet project, provide feedback, join us in evolving the tool, and participate with our team in the community.
Learn more about DataSonnet:
Let us know what you think about it in the comments below!
Recent posts

Stablecoin Regulation for Banking: How Governed Architecture Becomes Competitive Advantage

Digital Money Economics: How Stablecoins and Tokenized Deposits Modernize Bank Payment Rails

Stablecoins 101: What Every Bank Leader Needs to Know Post-GENIUS Act

Banking APIs for Embedded Finance Decide Whether Your Bank or Credit Union Is Essential to Business Customers
© PortX, Inc. 2022-2026. All Rights Reserved.
© PortX, Inc. 2022-2026. All Rights Reserved.



PortX complies with industry-standard
controls and is SOC2 certified.
PortX complies with industry-standard
controls and is SOC2 certified.