You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/05/02 08:12:54 UTC

(camel) 01/01: CAMEL-23381: camel-core - Simple language inlined result type for a few common types

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch sc
in repository https://gitbox.apache.org/repos/asf/camel.git

commit ce55608d865d212f3bc689f05711a18f52369504
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat May 2 10:12:32 2026 +0200

    CAMEL-23381: camel-core - Simple language inlined result type for a few common types
---
 .../org/apache/camel/catalog/CamelCatalogTest.java |  9 ++++
 .../jsonpath/JsonPathSimpleResultTypeTest.java     | 57 ++++++++++++++++++++++
 .../modules/languages/pages/simple-language.adoc   | 25 ++++++++++
 .../simple/ast/SimpleFunctionExpression.java       | 29 ++++++++++-
 .../apache/camel/language/simple/SimpleTest.java   | 23 +++++++++
 5 files changed, 142 insertions(+), 1 deletion(-)

diff --git a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index 211e6873b138..2e15696f0f3d 100644
--- a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++ b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -1133,6 +1133,15 @@ public class CamelCatalogTest {
         result = catalog.validateLanguagePredicate(null, "simple", "${body.length} =!= 12");
         assertFalse(result.isSuccess());
         assertEquals("Unexpected token =", result.getShortError());
+
+        result = catalog.validateLanguageExpression(null, "simple", "${int:body}");
+        assertTrue(result.isSuccess());
+        assertEquals("${int:body}", result.getText());
+
+        result = catalog.validateLanguageExpression(null, "simple", "${unknown:body}");
+        assertFalse(result.isSuccess());
+        assertEquals("${unknown:body}", result.getText());
+        assertEquals("Unknown function: unknown:body", result.getShortError());
     }
 
     @Test
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleResultTypeTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleResultTypeTest.java
new file mode 100644
index 000000000000..fbd1523cf1b6
--- /dev/null
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleResultTypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.jsonpath;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class JsonPathSimpleResultTypeTest extends CamelTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .setHeader("age", simple("${int:jsonpath($.age)}"))
+                        .setHeader("id", simple("${long:jsonpath($.id)}"))
+                        .setHeader("name", simple("${string:jsonpath($.name)}"))
+                        .setHeader("vip", simple("${boolean:jsonpath($.vip)}"))
+                        .to("mock:result");
+            }
+        };
+    }
+
+    @Test
+    public void testInlinedResultType() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        getMockEndpoint("mock:result").expectedHeaderReceived("age", Integer.valueOf("42"));
+        getMockEndpoint("mock:result").message(0).header("age").isInstanceOf(Integer.class);
+        getMockEndpoint("mock:result").expectedHeaderReceived("id", Long.valueOf("1234567890"));
+        getMockEndpoint("mock:result").message(0).header("id").isInstanceOf(Long.class);
+        getMockEndpoint("mock:result").expectedHeaderReceived("name", "scott");
+        getMockEndpoint("mock:result").expectedHeaderReceived("vip", Boolean.valueOf("true"));
+        getMockEndpoint("mock:result").message(0).header("vip").isInstanceOf(Boolean.class);
+
+        template.sendBody("direct:start", "{\"id\": \"1234567890\", \"age\": \"42\", \"name\": \"scott\", \"vip\": \"true\"}");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+}
diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index ccceb2ce08f3..dd7d05762846 100644
--- a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -122,6 +122,31 @@ The Simple language has many built-in functions which allows access to various p
 
 NOTE: Some functions take 1 or more arguments enclosed in parentheses, and arguments separated by comma (ie `${replace('custID','customerId')}`.
 
+=== Inlined Result Type Function
+
+**Available as of Camel 4.21**
+
+The simple language, has many built-in functions, and each of them take different set of parameters, and some are more advanced than others, for example
+the functions working with JSon or XML payloads. Regards of that, you may have need for being able to easily specify that the function should return the value
+as an integer instead of _what it would otherwise do_. To make this easy we have built-in a fixed set of very common result types to make this easy.
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|====
+|Prefix |Response Type |Description
+|`int:` | `Integer` | To use `Integer` as a result type.
+|`integer:` | `Integer` | To use `Integer` as a result type.
+|`long:` | `Long` | To use `Long` as a result type.
+|`boolean:` | `Boolean` | To use `Boolean` as a result type.
+|`string:` | `String` | To use `String` (text) as a result type.
+|====
+
+You do this by prefixing the function with any of the above, such as `${int:header.age}` to return the age header as an integer.
+If you use XML then the XPath function can do this as well such as `${int:xpath(//food/quantity)`.
+
+If you need to use a type that is not in the table above, then you can combine two functions together with the `${convertTo(type)}` function, as follows:
+
+`${xpath(//food[name='food1']/price/text())} ~> ${convertTo(java.math.BigDecimal)}`
+
 === Core Camel Functions
 
 The Camel functions are as the name implies specific to Apache Camel and these functions are primary used
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index c3d1f9528ae8..825478e0fd76 100644
--- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -81,6 +81,33 @@ public class SimpleFunctionExpression extends LiteralExpression {
     }
 
     private Expression createSimpleExpression(CamelContext camelContext, String function, boolean strict) {
+        Class<?> type = null;
+
+        // is it a known result type (make it easy in simple to return the value as you need)
+        if (function.startsWith("int:")) {
+            type = int.class;
+            function = function.substring(4);
+        } else if (function.startsWith("integer:")) {
+            type = int.class;
+            function = function.substring(8);
+        } else if (function.startsWith("long:")) {
+            type = long.class;
+            function = function.substring(5);
+        } else if (function.startsWith("boolean:")) {
+            type = boolean.class;
+            function = function.substring(8);
+        } else if (function.startsWith("string:")) {
+            type = String.class;
+            function = function.substring(7);
+        }
+        Expression exp = doCreateSimpleExpression(camelContext, function, strict);
+        if (type != null) {
+            exp = ExpressionBuilder.convertToExpression(exp, type);
+        }
+        return exp;
+    }
+
+    private Expression doCreateSimpleExpression(CamelContext camelContext, String function, boolean strict) {
         // return the function directly if we can create function without analyzing the prefix
         Expression answer = createSimpleExpressionDirectly(camelContext, function);
         if (answer != null) {
@@ -713,7 +740,7 @@ public class SimpleFunctionExpression extends LiteralExpression {
             String exp = StringHelper.beforeLast(remainder, ")");
             if (exp == null) {
                 throw new SimpleParserException(
-                        "Valid syntax: ${simpleJsonpath(input,exp)} was: " + function, token.getIndex());
+                        "Valid syntax: ${simpleJsonpath(exp)} was: " + function, token.getIndex());
             }
             String input = null;
             exp = StringHelper.removeLeadingAndEndingQuotes(exp);
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index 71522c9f59e0..1bd1a04f4faa 100644
--- a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -2738,6 +2738,29 @@ public class SimpleTest extends LanguageTestSupport {
         assertEquals(Boolean.TRUE, s);
     }
 
+    @Test
+    public void testInlinedResultType() {
+        exchange.getMessage().setBody("123");
+        Expression expression = context.resolveLanguage("simple").createExpression("${int:body}");
+        Object s = expression.evaluate(exchange, Object.class);
+        assertIsInstanceOf(Integer.class, s);
+        expression = context.resolveLanguage("simple").createExpression("${integer:body}");
+        s = expression.evaluate(exchange, Object.class);
+        assertIsInstanceOf(Integer.class, s);
+        expression = context.resolveLanguage("simple").createExpression("${long:body}");
+        s = expression.evaluate(exchange, Object.class);
+        assertIsInstanceOf(Long.class, s);
+
+        exchange.getMessage().setBody("true");
+        expression = context.resolveLanguage("simple").createExpression("${boolean:body}");
+        s = expression.evaluate(exchange, Object.class);
+        assertIsInstanceOf(Boolean.class, s);
+
+        exchange.getMessage().setBody("Hello World");
+        expression = context.resolveLanguage("simple").createExpression("${string:body}");
+        s = expression.evaluate(exchange, String.class);
+    }
+
     @Test
     public void testConvertToCamelJson() {
         exchange.getMessage().setBody("{ \"name\": \"Scott\", \"age\": 44 }");