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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7e8472d14714 CAMEL-23170: camel-core - Add simple jsonpath to Camels 
JsonObject (#21916)
7e8472d14714 is described below

commit 7e8472d14714d3224a090621a7f64a4ea59af249
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Mar 11 12:01:28 2026 +0100

    CAMEL-23170: camel-core - Add simple jsonpath to Camels JsonObject (#21916)
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels JsonObject
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels JsonObject
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels simple language 
for very basic jsonpath needs without learning jq/jsonpath lingo
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels simple language 
for very basic jsonpath needs without learning jq/jsonpath lingo
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels simple language 
for very basic jsonpath needs without learning jq/jsonpath lingo
    
    * CAMEL-23170: camel-core - Add simple jsonpath to Camels simple language 
for very basic jsonpath needs without learning jq/jsonpath lingo
---
 .../org/apache/camel/catalog/languages/simple.json |  49 ++---
 .../org/apache/camel/language/simple/simple.json   |  49 ++---
 .../modules/languages/pages/simple-language.adoc   |  77 +++++++-
 .../camel/language/simple/SimpleConstants.java     |   4 +
 .../language/simple/SimpleExpressionBuilder.java   |  34 ++++
 .../simple/ast/SimpleFunctionExpression.java       |  23 +++
 .../camel/converter/json/JsonConverterTest.java    |  62 +++++++
 .../language/simple/FileSimpleJsonPathTest.java    |  77 ++++++++
 .../apache/camel/language/simple/SimpleTest.java   |  51 ++++++
 core/camel-core/src/test/resources/books.json      |  20 ++
 .../stream/CamelSupportBulkConverterLoader.java    |  86 ++++++++-
 .../apache/camel/converter/json/JsonConverter.java |  90 +++++++++
 .../camel/support/builder/ExpressionBuilder.java   |  12 +-
 .../java/org/apache/camel/util/SimpleUtils.java    |   1 +
 .../org/apache/camel/util/json/JsonObject.java     | 201 +++++++++++++++++++++
 .../apache/camel/util/json/JsonObjectPathTest.java | 175 ++++++++++++++++++
 16 files changed, 954 insertions(+), 57 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
index 9719e79478a4..8fd2a4bc662a 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json
@@ -126,29 +126,30 @@
     "setHeader(name,type,exp)": { "index": 98, "kind": "function", 
"displayName": "Set Header", "group": "core", "label": "core", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sets a message header with the given expression (optional converting to the 
given type)", "ognl": false, "suffix": "}" },
     "setVariable(name,type,exp)": { "index": 99, "kind": "function", 
"displayName": "Set Variable", "group": "core", "label": "core", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sets a variable with the given expression (optional converting to the given 
type)", "ognl": false, "suffix": "}" },
     "shuffle(val...)": { "index": 100, "kind": "function", "displayName": 
"Shuffle Values", "group": "collection", "label": "collection", "required": 
false, "javaType": "List", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a list of all the values shuffled in random order", "ognl": false, 
"suffix": "}" },
-    "size(exp)": { "index": 101, "kind": "function", "displayName": "Size", 
"group": "collection", "label": "collection", "required": false, "javaType": 
"int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Returns the number of elements in 
collection or array based payloads. If the value is null then 0 is returned, 
otherwise 1.", "ognl": false, "suffix": "}" },
-    "skip(num)": { "index": 102, "kind": "function", "displayName": "Skip 
First Items from the Message Body", "group": "collection", "label": 
"collection", "required": false, "javaType": "java.util.Iterator", "prefix": 
"${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The skip function iterates the message body and skips 
the first number of items. This can be used with the Splitter EIP to split a 
message body and skip the first N numbe [...]
-    "split(exp,separator)": { "index": 103, "kind": "function", "displayName": 
"Split String Values", "group": "collection", "label": "collection", 
"required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Splits the message body\/expression as a String value using the separator into 
a String array", "ognl": false, "suffix": "}" },
-    "stepId": { "index": 104, "kind": "function", "displayName": "Step Id", 
"group": "core", "label": "core", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current step the 
Exchange is being routed.", "ognl": false, "suffix": "}" },
-    "substring(head,tail)": { "index": 105, "kind": "function", "displayName": 
"Substring", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Returns a substring of 
the message body\/expression. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped from  [...]
-    "substringAfter(exp,before)": { "index": 106, "kind": "function", 
"displayName": "Substring After", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
-    "substringBefore(exp,before)": { "index": 107, "kind": "function", 
"displayName": "Substring Before", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
-    "substringBetween(exp,after,before)": { "index": 108, "kind": "function", 
"displayName": "Substring Between", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that are between after and 
before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" },
-    "sum(val...)": { "index": 109, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "number", "label": "number", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
-    "sys.name": { "index": 110, "kind": "function", "displayName": "JVM System 
Property", "group": "other", "label": "other", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The JVM system property 
with the given name", "ognl": false, "suffix": "}" },
-    "threadId": { "index": 111, "kind": "function", "displayName": "Thread 
Id", "group": "other", "label": "other", "required": false, "javaType": "long", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current thread. Can be 
used for logging.", "ognl": false, "suffix": "}" },
-    "threadName": { "index": 112, "kind": "function", "displayName": "Thread 
Name", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "throwException(type,msg)": { "index": 113, "kind": "function", 
"displayName": "Throw Exception", "group": "core", "label": "core", "required": 
false, "javaType": "java.lang.Exception", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Deliberately throws an error. Uses IllegalArgumentException by default if no 
type is specified (use fully qualified classname).", "ognl": false, "suffix": 
"}" },
-    "trim(exp)": { "index": 114, "kind": "function", "displayName": "Trim", 
"group": "string", "label": "string", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The trim function trims the message body (or 
expression) by removing all leading and trailing white spaces.", "ognl": false, 
"suffix": "}" },
-    "type:name.field": { "index": 115, "kind": "function", "displayName": 
"Java Field Value", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", "ognl":  [...]
-    "kindOfType(exp)": { "index": 116, "kind": "function", "displayName": 
"Kind of Type", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
-    "unquote(exp)": { "index": 117, "kind": "function", "displayName": 
"Unquote", "group": "string", "label": "string", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the message body 
(or expression) with any leading\/ending quotes removed", "ognl": false, 
"suffix": "}" },
-    "uppercase(exp)": { "index": 118, "kind": "function", "displayName": 
"Uppercase", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Uppercases the message 
body (or expression)", "ognl": false, "suffix": "}" },
-    "uuid(type)": { "index": 119, "kind": "function", "displayName": "Generate 
UUID", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidGenera [...]
-    "val(exp)": { "index": 120, "kind": "function", "displayName": "Value", 
"group": "core", "label": "core", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the expression as a constant value", 
"ognl": false, "suffix": "}" },
-    "variable.name": { "index": 121, "kind": "function", "displayName": 
"Variable", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
-    "variableAs(key,type)": { "index": 122, "kind": "function", "displayName": 
"Variable As", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the variable to 
the given type (classname).", "ognl": false, "suffix": "}" },
-    "variables": { "index": 123, "kind": "function", "displayName": 
"Variables", "group": "core", "label": "core", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the variables 
from the current Exchange in a Map", "ognl": false, "suffix": "}" },
-    "xpath(input,exp)": { "index": 124, "kind": "function", "displayName": 
"XPath", "group": "xml", "label": "xml", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "When working with XML 
data, then this allows using the XPath language, for example, to extract data 
from the message body (in XML format). This requires having camel-xpath JAR on 
the classpath. For input (optional), you ca [...]
+    "simpleJsonpath(input,exp)": { "index": 101, "kind": "function", 
"displayName": "Simple JSonPath", "group": "json", "label": "json", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"When working with JSon data, then this allows using the Simple JSonPath 
language, for example, to extract data from the message body (in JSon format). 
For input (optional), you can choose `header:key`, [...]
+    "size(exp)": { "index": 102, "kind": "function", "displayName": "Size", 
"group": "collection", "label": "collection", "required": false, "javaType": 
"int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Returns the number of elements in 
collection or array based payloads. If the value is null then 0 is returned, 
otherwise 1.", "ognl": false, "suffix": "}" },
+    "skip(num)": { "index": 103, "kind": "function", "displayName": "Skip 
First Items from the Message Body", "group": "collection", "label": 
"collection", "required": false, "javaType": "java.util.Iterator", "prefix": 
"${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The skip function iterates the message body and skips 
the first number of items. This can be used with the Splitter EIP to split a 
message body and skip the first N numbe [...]
+    "split(exp,separator)": { "index": 104, "kind": "function", "displayName": 
"Split String Values", "group": "collection", "label": "collection", 
"required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Splits the message body\/expression as a String value using the separator into 
a String array", "ognl": false, "suffix": "}" },
+    "stepId": { "index": 105, "kind": "function", "displayName": "Step Id", 
"group": "core", "label": "core", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current step the 
Exchange is being routed.", "ognl": false, "suffix": "}" },
+    "substring(head,tail)": { "index": 106, "kind": "function", "displayName": 
"Substring", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Returns a substring of 
the message body\/expression. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped from  [...]
+    "substringAfter(exp,before)": { "index": 107, "kind": "function", 
"displayName": "Substring After", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
+    "substringBefore(exp,before)": { "index": 108, "kind": "function", 
"displayName": "Substring Before", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
+    "substringBetween(exp,after,before)": { "index": 109, "kind": "function", 
"displayName": "Substring Between", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that are between after and 
before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" },
+    "sum(val...)": { "index": 110, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "number", "label": "number", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
+    "sys.name": { "index": 111, "kind": "function", "displayName": "JVM System 
Property", "group": "other", "label": "other", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The JVM system property 
with the given name", "ognl": false, "suffix": "}" },
+    "threadId": { "index": 112, "kind": "function", "displayName": "Thread 
Id", "group": "other", "label": "other", "required": false, "javaType": "long", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current thread. Can be 
used for logging.", "ognl": false, "suffix": "}" },
+    "threadName": { "index": 113, "kind": "function", "displayName": "Thread 
Name", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "throwException(type,msg)": { "index": 114, "kind": "function", 
"displayName": "Throw Exception", "group": "core", "label": "core", "required": 
false, "javaType": "java.lang.Exception", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Deliberately throws an error. Uses IllegalArgumentException by default if no 
type is specified (use fully qualified classname).", "ognl": false, "suffix": 
"}" },
+    "trim(exp)": { "index": 115, "kind": "function", "displayName": "Trim", 
"group": "string", "label": "string", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The trim function trims the message body (or 
expression) by removing all leading and trailing white spaces.", "ognl": false, 
"suffix": "}" },
+    "type:name.field": { "index": 116, "kind": "function", "displayName": 
"Java Field Value", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", "ognl":  [...]
+    "kindOfType(exp)": { "index": 117, "kind": "function", "displayName": 
"Kind of Type", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
+    "unquote(exp)": { "index": 118, "kind": "function", "displayName": 
"Unquote", "group": "string", "label": "string", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the message body 
(or expression) with any leading\/ending quotes removed", "ognl": false, 
"suffix": "}" },
+    "uppercase(exp)": { "index": 119, "kind": "function", "displayName": 
"Uppercase", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Uppercases the message 
body (or expression)", "ognl": false, "suffix": "}" },
+    "uuid(type)": { "index": 120, "kind": "function", "displayName": "Generate 
UUID", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidGenera [...]
+    "val(exp)": { "index": 121, "kind": "function", "displayName": "Value", 
"group": "core", "label": "core", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the expression as a constant value", 
"ognl": false, "suffix": "}" },
+    "variable.name": { "index": 122, "kind": "function", "displayName": 
"Variable", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
+    "variableAs(key,type)": { "index": 123, "kind": "function", "displayName": 
"Variable As", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the variable to 
the given type (classname).", "ognl": false, "suffix": "}" },
+    "variables": { "index": 124, "kind": "function", "displayName": 
"Variables", "group": "core", "label": "core", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the variables 
from the current Exchange in a Map", "ognl": false, "suffix": "}" },
+    "xpath(input,exp)": { "index": 125, "kind": "function", "displayName": 
"XPath", "group": "xml", "label": "xml", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "When working with XML 
data, then this allows using the XPath language, for example, to extract data 
from the message body (in XML format). This requires having camel-xpath JAR on 
the classpath. For input (optional), you ca [...]
   }
 }
diff --git 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
index 9719e79478a4..8fd2a4bc662a 100644
--- 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
+++ 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
@@ -126,29 +126,30 @@
     "setHeader(name,type,exp)": { "index": 98, "kind": "function", 
"displayName": "Set Header", "group": "core", "label": "core", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sets a message header with the given expression (optional converting to the 
given type)", "ognl": false, "suffix": "}" },
     "setVariable(name,type,exp)": { "index": 99, "kind": "function", 
"displayName": "Set Variable", "group": "core", "label": "core", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sets a variable with the given expression (optional converting to the given 
type)", "ognl": false, "suffix": "}" },
     "shuffle(val...)": { "index": 100, "kind": "function", "displayName": 
"Shuffle Values", "group": "collection", "label": "collection", "required": 
false, "javaType": "List", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a list of all the values shuffled in random order", "ognl": false, 
"suffix": "}" },
-    "size(exp)": { "index": 101, "kind": "function", "displayName": "Size", 
"group": "collection", "label": "collection", "required": false, "javaType": 
"int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Returns the number of elements in 
collection or array based payloads. If the value is null then 0 is returned, 
otherwise 1.", "ognl": false, "suffix": "}" },
-    "skip(num)": { "index": 102, "kind": "function", "displayName": "Skip 
First Items from the Message Body", "group": "collection", "label": 
"collection", "required": false, "javaType": "java.util.Iterator", "prefix": 
"${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The skip function iterates the message body and skips 
the first number of items. This can be used with the Splitter EIP to split a 
message body and skip the first N numbe [...]
-    "split(exp,separator)": { "index": 103, "kind": "function", "displayName": 
"Split String Values", "group": "collection", "label": "collection", 
"required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Splits the message body\/expression as a String value using the separator into 
a String array", "ognl": false, "suffix": "}" },
-    "stepId": { "index": 104, "kind": "function", "displayName": "Step Id", 
"group": "core", "label": "core", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current step the 
Exchange is being routed.", "ognl": false, "suffix": "}" },
-    "substring(head,tail)": { "index": 105, "kind": "function", "displayName": 
"Substring", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Returns a substring of 
the message body\/expression. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped from  [...]
-    "substringAfter(exp,before)": { "index": 106, "kind": "function", 
"displayName": "Substring After", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
-    "substringBefore(exp,before)": { "index": 107, "kind": "function", 
"displayName": "Substring Before", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
-    "substringBetween(exp,after,before)": { "index": 108, "kind": "function", 
"displayName": "Substring Between", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that are between after and 
before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" },
-    "sum(val...)": { "index": 109, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "number", "label": "number", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
-    "sys.name": { "index": 110, "kind": "function", "displayName": "JVM System 
Property", "group": "other", "label": "other", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The JVM system property 
with the given name", "ognl": false, "suffix": "}" },
-    "threadId": { "index": 111, "kind": "function", "displayName": "Thread 
Id", "group": "other", "label": "other", "required": false, "javaType": "long", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current thread. Can be 
used for logging.", "ognl": false, "suffix": "}" },
-    "threadName": { "index": 112, "kind": "function", "displayName": "Thread 
Name", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
-    "throwException(type,msg)": { "index": 113, "kind": "function", 
"displayName": "Throw Exception", "group": "core", "label": "core", "required": 
false, "javaType": "java.lang.Exception", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Deliberately throws an error. Uses IllegalArgumentException by default if no 
type is specified (use fully qualified classname).", "ognl": false, "suffix": 
"}" },
-    "trim(exp)": { "index": 114, "kind": "function", "displayName": "Trim", 
"group": "string", "label": "string", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The trim function trims the message body (or 
expression) by removing all leading and trailing white spaces.", "ognl": false, 
"suffix": "}" },
-    "type:name.field": { "index": 115, "kind": "function", "displayName": 
"Java Field Value", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", "ognl":  [...]
-    "kindOfType(exp)": { "index": 116, "kind": "function", "displayName": 
"Kind of Type", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
-    "unquote(exp)": { "index": 117, "kind": "function", "displayName": 
"Unquote", "group": "string", "label": "string", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the message body 
(or expression) with any leading\/ending quotes removed", "ognl": false, 
"suffix": "}" },
-    "uppercase(exp)": { "index": 118, "kind": "function", "displayName": 
"Uppercase", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Uppercases the message 
body (or expression)", "ognl": false, "suffix": "}" },
-    "uuid(type)": { "index": 119, "kind": "function", "displayName": "Generate 
UUID", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidGenera [...]
-    "val(exp)": { "index": 120, "kind": "function", "displayName": "Value", 
"group": "core", "label": "core", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the expression as a constant value", 
"ognl": false, "suffix": "}" },
-    "variable.name": { "index": 121, "kind": "function", "displayName": 
"Variable", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
-    "variableAs(key,type)": { "index": 122, "kind": "function", "displayName": 
"Variable As", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the variable to 
the given type (classname).", "ognl": false, "suffix": "}" },
-    "variables": { "index": 123, "kind": "function", "displayName": 
"Variables", "group": "core", "label": "core", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the variables 
from the current Exchange in a Map", "ognl": false, "suffix": "}" },
-    "xpath(input,exp)": { "index": 124, "kind": "function", "displayName": 
"XPath", "group": "xml", "label": "xml", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "When working with XML 
data, then this allows using the XPath language, for example, to extract data 
from the message body (in XML format). This requires having camel-xpath JAR on 
the classpath. For input (optional), you ca [...]
+    "simpleJsonpath(input,exp)": { "index": 101, "kind": "function", 
"displayName": "Simple JSonPath", "group": "json", "label": "json", "required": 
false, "javaType": "Object", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"When working with JSon data, then this allows using the Simple JSonPath 
language, for example, to extract data from the message body (in JSon format). 
For input (optional), you can choose `header:key`, [...]
+    "size(exp)": { "index": 102, "kind": "function", "displayName": "Size", 
"group": "collection", "label": "collection", "required": false, "javaType": 
"int", "prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Returns the number of elements in 
collection or array based payloads. If the value is null then 0 is returned, 
otherwise 1.", "ognl": false, "suffix": "}" },
+    "skip(num)": { "index": 103, "kind": "function", "displayName": "Skip 
First Items from the Message Body", "group": "collection", "label": 
"collection", "required": false, "javaType": "java.util.Iterator", "prefix": 
"${", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The skip function iterates the message body and skips 
the first number of items. This can be used with the Splitter EIP to split a 
message body and skip the first N numbe [...]
+    "split(exp,separator)": { "index": 104, "kind": "function", "displayName": 
"Split String Values", "group": "collection", "label": "collection", 
"required": false, "javaType": "String[]", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Splits the message body\/expression as a String value using the separator into 
a String array", "ognl": false, "suffix": "}" },
+    "stepId": { "index": 105, "kind": "function", "displayName": "Step Id", 
"group": "core", "label": "core", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current step the 
Exchange is being routed.", "ognl": false, "suffix": "}" },
+    "substring(head,tail)": { "index": 106, "kind": "function", "displayName": 
"Substring", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Returns a substring of 
the message body\/expression. If only one positive number, then the returned 
string is clipped from the beginning. If only one negative number, then the 
returned string is clipped from  [...]
+    "substringAfter(exp,before)": { "index": 107, "kind": "function", 
"displayName": "Substring After", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes after. Returns 
null if nothing comes after.", "ognl": false, "suffix": "}" },
+    "substringBefore(exp,before)": { "index": 108, "kind": "function", 
"displayName": "Substring Before", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that comes before. Returns 
null if nothing comes before.", "ognl": false, "suffix": "}" },
+    "substringBetween(exp,after,before)": { "index": 109, "kind": "function", 
"displayName": "Substring Between", "group": "string", "label": "string", 
"required": false, "javaType": "String", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Returns a substring of the message body\/expression that are between after and 
before. Returns null if nothing comes between.", "ognl": false, "suffix": "}" },
+    "sum(val...)": { "index": 110, "kind": "function", "displayName": 
"Calculate Sum Number", "group": "number", "label": "number", "required": 
false, "javaType": "long", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Sums together all the values as integral numbers. This function can also be 
used to subtract by using negative numbers.", "ognl": false, "suffix": "}" },
+    "sys.name": { "index": 111, "kind": "function", "displayName": "JVM System 
Property", "group": "other", "label": "other", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The JVM system property 
with the given name", "ognl": false, "suffix": "}" },
+    "threadId": { "index": 112, "kind": "function", "displayName": "Thread 
Id", "group": "other", "label": "other", "required": false, "javaType": "long", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current thread. Can be 
used for logging.", "ognl": false, "suffix": "}" },
+    "threadName": { "index": 113, "kind": "function", "displayName": "Thread 
Name", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the name of the 
current thread. Can be used for logging.", "ognl": false, "suffix": "}" },
+    "throwException(type,msg)": { "index": 114, "kind": "function", 
"displayName": "Throw Exception", "group": "core", "label": "core", "required": 
false, "javaType": "java.lang.Exception", "prefix": "${", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": 
"Deliberately throws an error. Uses IllegalArgumentException by default if no 
type is specified (use fully qualified classname).", "ognl": false, "suffix": 
"}" },
+    "trim(exp)": { "index": 115, "kind": "function", "displayName": "Trim", 
"group": "string", "label": "string", "required": false, "javaType": "String", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The trim function trims the message body (or 
expression) by removing all leading and trailing white spaces.", "ognl": false, 
"suffix": "}" },
+    "type:name.field": { "index": 116, "kind": "function", "displayName": 
"Java Field Value", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "To refer to a type or 
field by its classname. To refer to a field, you can append .FIELD_NAME. For 
example, you can refer to the constant field from Exchange as: 
`org.apache.camel.Exchange.FILE_NAME`", "ognl":  [...]
+    "kindOfType(exp)": { "index": 117, "kind": "function", "displayName": 
"Kind of Type", "group": "core", "label": "core", "required": false, 
"javaType": "Object", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "What kind of type is 
the value (null,number,string,boolean,array,object)", "ognl": false, "suffix": 
"}" },
+    "unquote(exp)": { "index": 118, "kind": "function", "displayName": 
"Unquote", "group": "string", "label": "string", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns the message body 
(or expression) with any leading\/ending quotes removed", "ognl": false, 
"suffix": "}" },
+    "uppercase(exp)": { "index": 119, "kind": "function", "displayName": 
"Uppercase", "group": "string", "label": "string", "required": false, 
"javaType": "String", "prefix": "${", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "description": "Uppercases the message 
body (or expression)", "ognl": false, "suffix": "}" },
+    "uuid(type)": { "index": 120, "kind": "function", "displayName": "Generate 
UUID", "group": "other", "label": "other", "required": false, "javaType": 
"String", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns a UUID using the 
Camel `UuidGenerator`. You can choose between `default`, `classic`, `short` and 
`simple` as the type. If no type is given, the default is used. It is also 
possible to use a custom `UuidGenera [...]
+    "val(exp)": { "index": 121, "kind": "function", "displayName": "Value", 
"group": "core", "label": "core", "required": false, "javaType": "Object", 
"prefix": "${", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the expression as a constant value", 
"ognl": false, "suffix": "}" },
+    "variable.name": { "index": 122, "kind": "function", "displayName": 
"Variable", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The variable with the 
given name", "ognl": true, "suffix": "}" },
+    "variableAs(key,type)": { "index": 123, "kind": "function", "displayName": 
"Variable As", "group": "core", "label": "core", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Converts the variable to 
the given type (classname).", "ognl": false, "suffix": "}" },
+    "variables": { "index": 124, "kind": "function", "displayName": 
"Variables", "group": "core", "label": "core", "required": false, "javaType": 
"java.util.Map", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Returns all the variables 
from the current Exchange in a Map", "ognl": false, "suffix": "}" },
+    "xpath(input,exp)": { "index": 125, "kind": "function", "displayName": 
"XPath", "group": "xml", "label": "xml", "required": false, "javaType": 
"Object", "prefix": "${", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "When working with XML 
data, then this allows using the XPath language, for example, to extract data 
from the message body (in XML format). This requires having camel-xpath JAR on 
the classpath. For input (optional), you ca [...]
   }
 }
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 3c970ec29400..3e0a9c959933 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
@@ -613,6 +613,8 @@ Functions to work with XML and JSon payloads.
 |`jsonpath(input,exp)` | `Object` | Same as `jsonpath(exp)` but to use the 
_input_ expression as the source of the JSon document.
 |`pretty(exp)` | `String` | Converts the expression to a `String`, and 
attempts to pretty print (if JSon or XML) otherwise return the value as-is.
 |`prettyBody` | `String` | Converts the message body to a `String`, and 
attempts to pretty print (if JSon or XML) otherwise return the value as-is.
+|`simpleJsonpath(exp)` | `Object` | When working with JSon data, then this 
allows using built-in Simple JsonPath, for example, to extract data from the 
message body (in JSon format).
+|`simpleJsonpath(input,exp)` | `Object` | Same as `simpleJsonpath(exp)` but to 
use the _input_ expression as the source of the JSon document.
 |`toJson(exp)` | `String` | Converts the expression to a JSon `String` 
representation. String values are returned as-is, null values return null, all 
other types are serialized to JSon.
 |`toJsonBody` | `String` | Converts the message body to a JSon `String` 
representation. String values are returned as-is, null values return null, all 
other types are serialized to JSon.
 |`toPrettyJson(exp)` | `String` | Converts the expression to a JSon `String` 
representation. String values are returned as-is, null values return null, all 
other types are serialized to JSon in pretty mode.
@@ -666,7 +668,79 @@ from("direct:map")
         .to("log:data");
 ----
 
-The `toJson` function is to convert the payload to JSon as a `String` value. 
However if the payload is already a `String` then no conversion happens.
+The `simpleJsonpath` function is using Camel's built in JSon library from 
`camel-util-json` that is a basic JSon parser.
+The syntax for the path is similar to Camel's bean OGNL syntax to use a dot 
notation to walk down a nested structure.
+
+Because the input JSon is not nested then the mapping example is just:
+
+[source,java]
+----
+from("direct:map")
+        .transform().simple("""
+            {
+              "roll": ${simpleJsonpath(id)},
+              "years": ${simpleJsonpath(age)},
+              "fullname": "${simpleJsonpath(name)}"
+            }""")
+        .to("log:data");
+----
+
+However, if there are nested JSon such as:
+
+[source,json]
+----
+{
+    "library": {
+        "book": [
+            {
+                "title": "No Title",
+                "author": "F. Scott Fitzgerald",
+                "year": "1925",
+                "genre": "Classic",
+                "movie": false,
+                "id": "bk101"
+            },
+            {
+                "title": "1984",
+                "author": "George Orwell",
+                "year": "1949",
+                "genre": "Dystopian",
+                "movie": true,
+                "id": "bk102"
+            }
+        ]
+    }
+}
+----
+
+Then you can retrieve data from the JSon payload as follows:
+
+[source,text]
+----
+library.book[0].title
+library.book[0].author
+library.book[0].year
+
+library.book[1].title
+library.book[1].author
+library.book[1].year
+----
+
+TIP: You can use `[last]` to refer to the last element in an array.
+
+You can use `?.` to declare that the path is option, so if the node or 
attribute does not exist, then `null` is returned.
+Otherwise, Camel will throw an exception.
+
+For example, we attempt to refer to a non-existing attribute
+
+[source,text]
+----
+library.book[0]?.cheese
+----
+
+The optional marker can also be used sooner in the path, such as 
`foo?.bar.age` which will not fail if there are no nested _bar_ node in the 
JSon payload.
+
+The `toJson` function is to convert the payload to JSon as a `String` value. 
However, if the payload is already a `String` then no conversion happens.
 So suppose the payload is a `Map` object as follows:
 
 [source,java]
@@ -680,7 +754,6 @@ Then `toJson` will output: `{"name":"Jack","id":123}`.
 
 There are also _pretty_ alternatives so when calling `toPrettyJson` will 
output: TODO:
 
-
 The `xpath` function is for working with XML and using XPath expressions to 
extract data from XML payloads.
 
 Given the following XML payload:
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
index 694e09659741..0b9475d6925b 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
@@ -410,6 +410,10 @@ public final class SimpleConstants {
               label = "collection", javaType = "List", displayName = "Shuffle 
Values")
     public static final String SHUFFLE = "shuffle(val...)";
 
+    @Metadata(description = "When working with JSon data, then this allows 
using the Simple JSonPath language, for example, to extract data from the 
message body (in JSon format). For input (optional), you can choose 
`header:key`, `exchangeProperty:key` or `variable:key` to use as input for the 
JSon payload instead of the message body.",
+              label = "json", javaType = "Object", displayName = "Simple 
JSonPath")
+    public static final String SIMPLE_JSONPATH = "simpleJsonpath(input,exp)";
+
     @Metadata(description = "Returns the number of elements in collection or 
array based payloads. If the value is null then 0 is returned, otherwise 1.",
               label = "collection", javaType = "int", displayName = "Size")
     public static final String SIZE = "size(exp)";
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index d7676516c22b..38a2d575a9d6 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -72,6 +72,7 @@ import org.apache.camel.util.OgnlHelper;
 import org.apache.camel.util.SkipIterator;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.StringQuoteHelper;
+import org.apache.camel.util.json.JsonObject;
 
 /**
  * Expression builder used by the simple language.
@@ -3359,6 +3360,9 @@ public final class SimpleExpressionBuilder {
         };
     }
 
+    /**
+     * Executes a custom simple function
+     */
     public static Expression customFunction(final String name, final String 
parameter) {
         return new ExpressionAdapter() {
             private Expression func;
@@ -3395,6 +3399,36 @@ public final class SimpleExpressionBuilder {
         };
     }
 
+    /**
+     * Evaluates the simple jsonpath with the source input
+     */
+    public static Expression simpleJsonPathExpression(String source, String 
path) {
+        return new ExpressionAdapter() {
+            private Expression input;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                JsonObject jo = input.evaluate(exchange, JsonObject.class);
+                if (jo != null) {
+                    return jo.path(path);
+                }
+                return null;
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                super.init(context);
+                input = ExpressionBuilder.singleInputExpression(source);
+                input.init(context);
+            }
+
+            @Override
+            public String toString() {
+                return "simpleJsonpath[" + path + "]";
+            }
+        };
+    }
+
     /**
      * Expression adapter for OGNL expression from Message Header or Exchange 
property
      */
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 53428c4ba90f..cf6ca3f744a6 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
@@ -688,6 +688,29 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             }
             return ExpressionBuilder.languageExpression("jq", exp);
         }
+        // simple-jsonpath
+        remainder = ifStartsWithReturnRemainder("simpleJsonpath(", function);
+        if (remainder != null) {
+            String exp = StringHelper.beforeLast(remainder, ")");
+            if (exp == null) {
+                throw new SimpleParserException(
+                        "Valid syntax: ${simpleJsonpath(input,exp)} was: " + 
function, token.getIndex());
+            }
+            String input = null;
+            exp = StringHelper.removeLeadingAndEndingQuotes(exp);
+            if (exp.startsWith("header:") || exp.startsWith("property:") || 
exp.startsWith("exchangeProperty:")
+                    || exp.startsWith("variable:")) {
+                input = StringHelper.before(exp, ",");
+                exp = StringHelper.after(exp, ",");
+                if (input != null) {
+                    input = input.trim();
+                }
+                if (exp != null) {
+                    exp = exp.trim();
+                }
+            }
+            return SimpleExpressionBuilder.simpleJsonPathExpression(input, 
exp);
+        }
         // jsonpath
         remainder = ifStartsWithReturnRemainder("jsonpath(", function);
         if (remainder != null) {
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/converter/json/JsonConverterTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/converter/json/JsonConverterTest.java
index 627b18a2036a..9ebc749e7522 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/converter/json/JsonConverterTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/converter/json/JsonConverterTest.java
@@ -16,7 +16,10 @@
  */
 package org.apache.camel.converter.json;
 
+import java.io.ByteArrayInputStream;
+
 import org.apache.camel.ContextTestSupport;
+import org.apache.camel.converter.stream.ByteArrayInputStreamCache;
 import org.apache.camel.model.ConvertBodyDefinition;
 import org.apache.camel.model.FromDefinition;
 import org.apache.camel.model.RouteDefinition;
@@ -138,4 +141,63 @@ public class JsonConverterTest extends ContextTestSupport {
         Assertions.assertEquals(1949, b2.getInteger("year"));
     }
 
+    @Test
+    public void testConvertObjectFromByteArray() throws Exception {
+        JsonObject jo = context.getTypeConverter().convertTo(JsonObject.class, 
BOOKS.getBytes());
+        Assertions.assertNotNull(jo);
+
+        JsonArray arr = jo.getJsonObject("library").getJsonArray("book");
+        Assertions.assertEquals(2, arr.size());
+        JsonObject b1 = arr.getJsonObject(0);
+        JsonObject b2 = arr.getJsonObject(1);
+        Assertions.assertEquals("No Title", b1.getString("title"));
+        Assertions.assertEquals(1925, b1.getInteger("year"));
+        Assertions.assertEquals("1984", b2.getString("title"));
+        Assertions.assertEquals(1949, b2.getInteger("year"));
+    }
+
+    @Test
+    public void testConvertObjectFromInputStream() throws Exception {
+        JsonObject jo = context.getTypeConverter().convertTo(JsonObject.class, 
new ByteArrayInputStream(BOOKS.getBytes()));
+        Assertions.assertNotNull(jo);
+
+        JsonArray arr = jo.getJsonObject("library").getJsonArray("book");
+        Assertions.assertEquals(2, arr.size());
+        JsonObject b1 = arr.getJsonObject(0);
+        JsonObject b2 = arr.getJsonObject(1);
+        Assertions.assertEquals("No Title", b1.getString("title"));
+        Assertions.assertEquals(1925, b1.getInteger("year"));
+        Assertions.assertEquals("1984", b2.getString("title"));
+        Assertions.assertEquals(1949, b2.getInteger("year"));
+    }
+
+    @Test
+    public void testConvertObjectFromStreamCacheInputStream() throws Exception 
{
+        ByteArrayInputStreamCache sc = new ByteArrayInputStreamCache(new 
ByteArrayInputStream(BOOKS.getBytes()));
+        JsonObject jo = context.getTypeConverter().convertTo(JsonObject.class, 
sc);
+        Assertions.assertNotNull(jo);
+        JsonArray arr = jo.getJsonObject("library").getJsonArray("book");
+        Assertions.assertEquals(2, arr.size());
+        JsonObject b1 = arr.getJsonObject(0);
+        JsonObject b2 = arr.getJsonObject(1);
+        Assertions.assertEquals("No Title", b1.getString("title"));
+        Assertions.assertEquals(1925, b1.getInteger("year"));
+        Assertions.assertEquals("1984", b2.getString("title"));
+        Assertions.assertEquals(1949, b2.getInteger("year"));
+
+        // should be able to reuse the stream cache again if we reset
+        sc.reset();
+        JsonObject jo2 = 
context.getTypeConverter().convertTo(JsonObject.class, sc);
+        Assertions.assertNotSame(jo, jo2); // not same instance
+        arr = jo2.getJsonObject("library").getJsonArray("book");
+        Assertions.assertEquals(2, arr.size());
+        b1 = arr.getJsonObject(0);
+        b2 = arr.getJsonObject(1);
+        Assertions.assertEquals("No Title", b1.getString("title"));
+        Assertions.assertEquals(1925, b1.getInteger("year"));
+        Assertions.assertEquals("1984", b2.getString("title"));
+        Assertions.assertEquals(1949, b2.getInteger("year"));
+
+    }
+
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/FileSimpleJsonPathTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/FileSimpleJsonPathTest.java
new file mode 100644
index 000000000000..4414e9d22dab
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/FileSimpleJsonPathTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.language.simple;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+public class FileSimpleJsonPathTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testFileConvertBodyToSimpleJsonPath() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                
from("file:src/test/resources?fileName=books.json&noop=true&initialDelay=0")
+                        .convertBodyTo(JsonObject.class)
+                        .setHeader("title1", 
simple("${simpleJsonpath(library.book[0].title)}"))
+                        .setHeader("title2", 
simple("${simpleJsonpath(library.book[1].title)}"))
+                        .to("mock:result");
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        
getMockEndpoint("mock:result").message(0).body().isInstanceOf(JsonObject.class);
+
+        getMockEndpoint("mock:result").expectedHeaderReceived("title1", "No 
Title");
+        getMockEndpoint("mock:result").expectedHeaderReceived("title2", 
"1984");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testFileSimpleJsonPath() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                
from("file:src/test/resources?fileName=books.json&noop=true&initialDelay=0")
+                        .setHeader("title1", 
simple("${simpleJsonpath(library.book[0].title)}"))
+                        .setHeader("title2", 
simple("${simpleJsonpath(library.book[1].title)}"))
+                        .to("mock:result");
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        
getMockEndpoint("mock:result").message(0).body().isInstanceOf(GenericFile.class);
+
+        getMockEndpoint("mock:result").expectedHeaderReceived("title1", "No 
Title");
+        getMockEndpoint("mock:result").expectedHeaderReceived("title2", 
"1984");
+
+        assertMockEndpointsSatisfied();
+    }
+
+}
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 042579a3dfa7..71522c9f59e0 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
@@ -82,6 +82,30 @@ public class SimpleTest extends LanguageTestSupport {
 
     private static final String INDEX_OUT_OF_BOUNDS_ERROR_MSG = "Index 2 out 
of bounds for length 2";
 
+    private static final String BOOKS
+            = """
+                    {
+                        "library": {
+                            "book": [
+                                {
+                                    "title": "No Title",
+                                    "author": "F. Scott Fitzgerald",
+                                    "year": "1925",
+                                    "genre": "Classic",
+                                    "id": "bk101"
+                                },
+                                {
+                                    "title": "1984",
+                                    "author": "George Orwell",
+                                    "year": "1949",
+                                    "genre": "Dystopian",
+                                    "id": "bk102"
+                                }
+                            ]
+                        }
+                    }
+                    """;
+
     @Override
     protected Registry createCamelRegistry() throws Exception {
         Registry jndi = super.createCamelRegistry();
@@ -4220,6 +4244,33 @@ public class SimpleTest extends LanguageTestSupport {
         assertEquals("object", s);
     }
 
+    @Test
+    public void testSimpleJsonpath() {
+        exchange.getMessage().setBody("{\"id\": 123, \"age\": 42, \"name\": 
\"scott\"}");
+        assertExpression("${simpleJsonpath(id)}", 123);
+        assertExpression("${simpleJsonpath(age)}", 42);
+        assertExpression("${simpleJsonpath(name)}", "scott");
+        assertExpression("${simpleJsonpath(?cheese)}", null);
+
+        exchange.getMessage().setBody(BOOKS);
+        assertExpression("${simpleJsonpath(library.book[0].title)}", "No 
Title");
+        assertExpression("${simpleJsonpath(library.book[0].year)}", 1925);
+        assertExpression("${simpleJsonpath(library.book[1].title)}", "1984");
+        assertExpression("${simpleJsonpath(library.book[1].year)}", 1949);
+        assertExpression("${simpleJsonpath(library?.book[2].year)}", null);
+
+        exchange.getMessage().setBody("Hello World");
+        exchange.getMessage().setHeader("books", BOOKS);
+        
assertExpression("${simpleJsonpath(header:books,library.book[0].title)}", "No 
Title");
+
+        exchange.setVariable("books2", BOOKS);
+        
assertExpression("${simpleJsonpath(variable:books2,library.book[1].title)}", 
"1984");
+
+        exchange.setProperty("books3", BOOKS);
+        
assertExpression("${simpleJsonpath(property:books3,library.book[1].year)}", 
1949);
+        
assertExpression("${simpleJsonpath(exchangeProperty:books3,library.book[1].author)}",
 "George Orwell");
+    }
+
     @Override
     protected String getLanguageName() {
         return "simple";
diff --git a/core/camel-core/src/test/resources/books.json 
b/core/camel-core/src/test/resources/books.json
new file mode 100644
index 000000000000..765a9aa78244
--- /dev/null
+++ b/core/camel-core/src/test/resources/books.json
@@ -0,0 +1,20 @@
+{
+  "library": {
+    "book": [
+      {
+        "title": "No Title",
+        "author": "F. Scott Fitzgerald",
+        "year": "1925",
+        "genre": "Classic",
+        "id": "bk101"
+      },
+      {
+        "title": "1984",
+        "author": "George Orwell",
+        "year": "1949",
+        "genre": "Dystopian",
+        "id": "bk102"
+      }
+    ]
+  }
+}
diff --git 
a/core/camel-support/src/generated/java/org/apache/camel/converter/stream/CamelSupportBulkConverterLoader.java
 
b/core/camel-support/src/generated/java/org/apache/camel/converter/stream/CamelSupportBulkConverterLoader.java
index ee06fd855ade..b30536432bb4 100644
--- 
a/core/camel-support/src/generated/java/org/apache/camel/converter/stream/CamelSupportBulkConverterLoader.java
+++ 
b/core/camel-support/src/generated/java/org/apache/camel/converter/stream/CamelSupportBulkConverterLoader.java
@@ -41,7 +41,7 @@ public final class CamelSupportBulkConverterLoader implements 
TypeConverterLoade
 
     @Override
     public int size() {
-        return 9;
+        return 21;
     }
 
     @Override
@@ -88,14 +88,50 @@ public final class CamelSupportBulkConverterLoader 
implements TypeConverterLoade
             if (value instanceof java.lang.String) {
                 return 
org.apache.camel.converter.json.JsonConverter.convertToJsonArray((java.lang.String)
 value, exchange);
             }
+            if (value instanceof byte[]) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonArray((byte[]) 
value, exchange);
+            }
+            if (value instanceof java.io.InputStream) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonArray((java.io.InputStream)
 value, exchange);
+            }
+            if (value instanceof java.io.File) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonArray((java.io.File) 
value, exchange);
+            }
+            if (value instanceof org.apache.camel.WrappedFile) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonArray((org.apache.camel.WrappedFile)
 value, exchange);
+            }
         } else if (to == org.apache.camel.util.json.JsonObject.class) {
             if (value instanceof java.lang.String) {
                 return 
org.apache.camel.converter.json.JsonConverter.convertToJsonObject((java.lang.String)
 value, exchange);
             }
+            if (value instanceof byte[]) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonObject((byte[]) 
value, exchange);
+            }
+            if (value instanceof java.io.InputStream) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonObject((java.io.InputStream)
 value, exchange);
+            }
+            if (value instanceof java.io.File) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonObject((java.io.File)
 value, exchange);
+            }
+            if (value instanceof org.apache.camel.WrappedFile) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJsonObject((org.apache.camel.WrappedFile)
 value, exchange);
+            }
         } else if (to == org.apache.camel.util.json.Jsonable.class) {
             if (value instanceof java.lang.String) {
                 return 
org.apache.camel.converter.json.JsonConverter.convertToJson((java.lang.String) 
value, exchange);
             }
+            if (value instanceof byte[]) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJson((byte[]) value, 
exchange);
+            }
+            if (value instanceof java.io.InputStream) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJson((java.io.InputStream)
 value, exchange);
+            }
+            if (value instanceof java.io.File) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJson((java.io.File) 
value, exchange);
+            }
+            if (value instanceof org.apache.camel.WrappedFile) {
+                return 
org.apache.camel.converter.json.JsonConverter.convertToJson((org.apache.camel.WrappedFile)
 value, exchange);
+            }
         }
         return null;
     }
@@ -108,8 +144,20 @@ public final class CamelSupportBulkConverterLoader 
implements TypeConverterLoade
         registry.addConverter(new 
TypeConvertible<>(org.apache.camel.converter.stream.CachedOutputStream.class, 
org.apache.camel.StreamCache.class), this);
         registry.addConverter(new TypeConvertible<>(java.io.Reader.class, 
org.apache.camel.StreamCache.class), this);
         registry.addConverter(new TypeConvertible<>(java.lang.String.class, 
org.apache.camel.util.json.JsonArray.class), this);
+        registry.addConverter(new TypeConvertible<>(byte[].class, 
org.apache.camel.util.json.JsonArray.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.InputStream.class, 
org.apache.camel.util.json.JsonArray.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.File.class, 
org.apache.camel.util.json.JsonArray.class), this);
+        registry.addConverter(new 
TypeConvertible<>(org.apache.camel.WrappedFile.class, 
org.apache.camel.util.json.JsonArray.class), this);
         registry.addConverter(new TypeConvertible<>(java.lang.String.class, 
org.apache.camel.util.json.JsonObject.class), this);
+        registry.addConverter(new TypeConvertible<>(byte[].class, 
org.apache.camel.util.json.JsonObject.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.InputStream.class, 
org.apache.camel.util.json.JsonObject.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.File.class, 
org.apache.camel.util.json.JsonObject.class), this);
+        registry.addConverter(new 
TypeConvertible<>(org.apache.camel.WrappedFile.class, 
org.apache.camel.util.json.JsonObject.class), this);
         registry.addConverter(new TypeConvertible<>(java.lang.String.class, 
org.apache.camel.util.json.Jsonable.class), this);
+        registry.addConverter(new TypeConvertible<>(byte[].class, 
org.apache.camel.util.json.Jsonable.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.InputStream.class, 
org.apache.camel.util.json.Jsonable.class), this);
+        registry.addConverter(new TypeConvertible<>(java.io.File.class, 
org.apache.camel.util.json.Jsonable.class), this);
+        registry.addConverter(new 
TypeConvertible<>(org.apache.camel.WrappedFile.class, 
org.apache.camel.util.json.Jsonable.class), this);
     }
 
     public TypeConverter lookup(Class<?> to, Class<?> from) {
@@ -138,14 +186,50 @@ public final class CamelSupportBulkConverterLoader 
implements TypeConverterLoade
             if (from == java.lang.String.class) {
                 return this;
             }
+            if (from == byte[].class) {
+                return this;
+            }
+            if (from == java.io.InputStream.class) {
+                return this;
+            }
+            if (from == java.io.File.class) {
+                return this;
+            }
+            if (from == org.apache.camel.WrappedFile.class) {
+                return this;
+            }
         } else if (to == org.apache.camel.util.json.JsonObject.class) {
             if (from == java.lang.String.class) {
                 return this;
             }
+            if (from == byte[].class) {
+                return this;
+            }
+            if (from == java.io.InputStream.class) {
+                return this;
+            }
+            if (from == java.io.File.class) {
+                return this;
+            }
+            if (from == org.apache.camel.WrappedFile.class) {
+                return this;
+            }
         } else if (to == org.apache.camel.util.json.Jsonable.class) {
             if (from == java.lang.String.class) {
                 return this;
             }
+            if (from == byte[].class) {
+                return this;
+            }
+            if (from == java.io.InputStream.class) {
+                return this;
+            }
+            if (from == java.io.File.class) {
+                return this;
+            }
+            if (from == org.apache.camel.WrappedFile.class) {
+                return this;
+            }
         }
         return null;
     }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/converter/json/JsonConverter.java
 
b/core/camel-support/src/main/java/org/apache/camel/converter/json/JsonConverter.java
index d522a5f791e7..3e6a91f60ac8 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/converter/json/JsonConverter.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/converter/json/JsonConverter.java
@@ -16,8 +16,17 @@
  */
 package org.apache.camel.converter.json;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
 import org.apache.camel.Converter;
 import org.apache.camel.Exchange;
+import org.apache.camel.WrappedFile;
+import org.apache.camel.support.ExchangeHelper;
+import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsonable;
@@ -50,4 +59,85 @@ public final class JsonConverter {
         return (Jsonable) Jsoner.deserialize(json);
     }
 
+    @Converter(order = 4)
+    public static JsonObject convertToJsonObject(byte[] json, Exchange 
exchange) throws Exception {
+        return Jsoner.deserialize(new String(json), (JsonObject) null);
+    }
+
+    @Converter(order = 5)
+    public static JsonArray convertToJsonArray(byte[] json, Exchange exchange) 
throws Exception {
+        return (JsonArray) Jsoner.deserialize(new String(json));
+    }
+
+    @Converter(order = 6)
+    public static Jsonable convertToJson(byte[] json, Exchange exchange) 
throws Exception {
+        return (Jsonable) Jsoner.deserialize(new String(json));
+    }
+
+    @Converter(order = 7)
+    public static JsonObject convertToJsonObject(InputStream is, Exchange 
exchange) throws Exception {
+        Reader reader = IOHelper.buffered(new InputStreamReader(is, 
ExchangeHelper.getCharset(exchange)));
+        return (JsonObject) Jsoner.deserialize(reader);
+    }
+
+    @Converter(order = 8)
+    public static JsonArray convertToJsonArray(InputStream is, Exchange 
exchange) throws Exception {
+        Reader reader = IOHelper.buffered(new InputStreamReader(is, 
ExchangeHelper.getCharset(exchange)));
+        return (JsonArray) Jsoner.deserialize(reader);
+    }
+
+    @Converter(order = 9)
+    public static Jsonable convertToJson(InputStream is, Exchange exchange) 
throws Exception {
+        Reader reader = IOHelper.buffered(new InputStreamReader(is, 
ExchangeHelper.getCharset(exchange)));
+        return (Jsonable) Jsoner.deserialize(reader);
+    }
+
+    @Converter(order = 10)
+    public static JsonObject convertToJsonObject(File file, Exchange exchange) 
throws Exception {
+        try (InputStream is = new FileInputStream(file)) {
+            return convertToJsonObject(is, exchange);
+        }
+    }
+
+    @Converter(order = 11)
+    public static JsonArray convertToJsonArray(File file, Exchange exchange) 
throws Exception {
+        try (InputStream is = new FileInputStream(file)) {
+            return convertToJsonArray(is, exchange);
+        }
+    }
+
+    @Converter(order = 12)
+    public static Jsonable convertToJson(File file, Exchange exchange) throws 
Exception {
+        try (InputStream is = new FileInputStream(file)) {
+            return convertToJson(is, exchange);
+        }
+    }
+
+    @Converter(order = 13)
+    public static JsonObject convertToJsonObject(WrappedFile wf, Exchange 
exchange) throws Exception {
+        Object body = wf.getFile();
+        if (body == null) {
+            body = wf.getBody();
+        }
+        return 
exchange.getContext().getTypeConverter().convertTo(JsonObject.class, exchange, 
body);
+    }
+
+    @Converter(order = 14)
+    public static JsonArray convertToJsonArray(WrappedFile wf, Exchange 
exchange) throws Exception {
+        Object body = wf.getFile();
+        if (body == null) {
+            body = wf.getBody();
+        }
+        return 
exchange.getContext().getTypeConverter().convertTo(JsonArray.class, exchange, 
body);
+    }
+
+    @Converter(order = 15)
+    public static Jsonable convertToJson(WrappedFile wf, Exchange exchange) 
throws Exception {
+        Object body = wf.getFile();
+        if (body == null) {
+            body = wf.getBody();
+        }
+        return 
exchange.getContext().getTypeConverter().convertTo(Jsonable.class, exchange, 
body);
+    }
+
 }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
index f4f94047e836..63a05598927a 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
@@ -1374,9 +1374,9 @@ public class ExpressionBuilder {
     /**
      * Creates a source {@link Expression} for languages that can accept input 
from other sources than the message body.
      *
-     * @param  source 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.
+     * @param  source Source to use, instead of message body. You can prefix 
with variable:, header:, property:, or
+     *                exchangeProperty: 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.
      * @return        a variable expression if {@code variableName} is not 
empty, a header expression if
      *                {@code headerName} is not empty, otherwise a property 
expression if {@code propertyName} is not
      *                empty or finally a body expression.
@@ -1389,6 +1389,8 @@ public class ExpressionBuilder {
             exp = headerExpression(source.substring(7), true);
         } else if (source.startsWith("property:")) {
             exp = exchangePropertyExpression(source.substring(9), true);
+        } else if (source.startsWith("exchangeProperty:")) {
+            exp = exchangePropertyExpression(source.substring(17), true);
         } else {
             if (source.startsWith("variable:")) {
                 source = source.substring(9);
@@ -1405,9 +1407,7 @@ public class ExpressionBuilder {
         return new ExpressionAdapter() {
             @Override
             public Object evaluate(Exchange exchange) {
-                // TODO When baseline will be Java 21
-                //                return Thread.currentThread().threadId();
-                return Thread.currentThread().getId();
+                return Thread.currentThread().threadId();
             }
 
             @Override
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/SimpleUtils.java 
b/core/camel-util/src/main/java/org/apache/camel/util/SimpleUtils.java
index dca2d08266f9..9297708d02b1 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SimpleUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SimpleUtils.java
@@ -121,6 +121,7 @@ public class SimpleUtils {
                     "setheader",
                     "setvariable",
                     "shuffle",
+                    "simplejsonpath",
                     "size",
                     "skip",
                     "sort",
diff --git 
a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
index e3c93873a2be..9cfce48e9924 100644
--- 
a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
+++ 
b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/JsonObject.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Optional;
 
 /**
  * JsonObject is a common non-thread safe data format for string to data 
mappings. The contents of a JsonObject are only
@@ -49,12 +50,212 @@ public class JsonObject extends LinkedHashMap<String, 
Object> implements Jsonabl
      * Instantiate a new JsonObject by accepting a map's entries, which could 
lead to de/serialization issues of the
      * resulting JsonObject since the entry values aren't validated as JSON 
values.
      *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
      * @param map represents the mappings to produce the JsonObject with.
      */
     public JsonObject(final Map<String, ?> map) {
         super(map);
     }
 
+    /**
+     * A very basic json-path that can retrieve leaf node using a dot syntax, 
such as "foo.bar.title", to get the title
+     * attribute from the leaf JsonObject object (ie bar).
+     *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
+     * The returned value is expected to be a String
+     *
+     * @throws IllegalArgumentException if the path traversal does not exist
+     */
+    public String pathString(final String path) {
+        Object returnable = path(path);
+        if (returnable instanceof Boolean) {
+            returnable = returnable.toString();
+        } else if (returnable instanceof Number) {
+            returnable = returnable.toString();
+        }
+        return (String) returnable;
+    }
+
+    /**
+     * A very basic json-path that can retrieve leaf node using a dot syntax, 
such as "foo.bar.title", to get the title
+     * attribute from the leaf JsonObject object (ie bar).
+     *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
+     * You can use ?. to mark a path as optional, which returns null instead 
of throwing an exception if the path
+     * traversal does not exist.
+     *
+     * The returned value is expected to be an Integer
+     *
+     * @throws IllegalArgumentException if the path traversal does not exist
+     */
+    public Integer pathInteger(String path) {
+        Object returnable = path(path);
+        if (returnable == null) {
+            return null;
+        }
+        if (returnable instanceof String) {
+            /* A String can be used to construct a BigDecimal. */
+            returnable = new BigDecimal((String) returnable);
+        }
+        return ((Number) returnable).intValue();
+    }
+
+    /**
+     * A very basic json-path that can retrieve leaf node using a dot syntax, 
such as "foo.bar.title", to get the title
+     * attribute from the leaf JsonObject object (ie bar).
+     *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
+     * You can use ?. to mark a path as optional, which returns null instead 
of throwing an exception if the path
+     * traversal does not exist.
+     *
+     * The returned value is expected to be a Boolean
+     *
+     * @throws IllegalArgumentException if the path traversal does not exist
+     */
+    public Boolean pathBoolean(String path) {
+        Object returnable = path(path);
+        if (returnable instanceof String) {
+            returnable = Boolean.valueOf((String) returnable);
+        }
+        return (Boolean) returnable;
+    }
+
+    /**
+     * A very basic json-path that can retrieve leaf node using a dot syntax, 
such as "foo.bar.title", to get the title
+     * attribute from the leaf JsonObject object (ie bar).
+     *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
+     * You can use ?. to mark a path as optional, which returns null instead 
of throwing an exception if the path
+     * traversal does not exist.
+     *
+     * The returned value can be an attribute or another JSonObject node
+     *
+     * @throws IllegalArgumentException if the path traversal does not exist
+     */
+    public Object path(final String path) {
+        Object answer = null;
+
+        boolean optional = path.startsWith("?");
+        JsonObject jo = this;
+        String sub = path;
+        if (optional && !path.contains(".") && !path.contains("[")) {
+            sub = sub.substring(1);
+        } else if (path.contains(".")) {
+            // grab until last dot
+            int pos = path.lastIndexOf(".");
+            optional = '?' == path.charAt(pos - 1);
+            sub = path.substring(pos + 1);
+            if (optional) {
+                pos = pos - 1;
+            }
+            java.util.Optional<Object> o = doPath(path.substring(0, pos));
+            if (o.isPresent()) {
+                answer = o.get();
+                if (answer instanceof JsonObject) {
+                    jo = (JsonObject) answer;
+                }
+            } else {
+                optional = true;
+                jo = null;
+            }
+        } else if (path.contains("[")) {
+            jo = null;
+            java.util.Optional<Object> o = doPath(path);
+            if (o.isPresent()) {
+                answer = o.get();
+                if (answer instanceof JsonObject) {
+                    jo = (JsonObject) answer;
+                }
+            } else {
+                optional = true;
+            }
+        }
+        if (jo != null) {
+            answer = jo.get(sub);
+        }
+        if (answer == null && !optional) {
+            throw new IllegalArgumentException("JSonObject path " + path + " 
is null");
+        }
+        return answer;
+    }
+
+    /**
+     * A very basic json-path that can retrieve leaf node using a dot syntax, 
such as "foo.bar.title", to get the title
+     * attribute from the leaf JsonObject object (ie bar).
+     *
+     * The json-path syntax also supports arrays using square brackets with 
the position index, such as
+     * "foo.bar[0].title", "foo.bar[1].title". You can use "last" as index 
number to get the last element of the array.
+     *
+     * You can use ?. to mark a path as optional, which returns null instead 
of throwing an exception if the path
+     * traversal does not exist.
+     *
+     * The returned value is a JsonObject
+     *
+     * @throws IllegalArgumentException if the path traversal does not exist
+     */
+    public JsonObject pathJsonObject(final String path) {
+        var o = doPath(path).orElse(null);
+        return JsonObject.class.cast(o);
+    }
+
+    private Optional<Object> doPath(final String path) {
+        Object answer = null;
+
+        String[] split = path.splitWithDelimiters("(\\?\\.|\\.)", 0);
+        for (int i = 0; i < split.length; i = i + 2) {
+            String part = split[i];
+            String dot = i > 0 ? split[i - 1] : null;
+            int pos = -1;
+            boolean optional;
+            if (part.startsWith("?")) {
+                part = part.substring(1);
+                optional = true;
+            } else {
+                optional = dot != null && dot.equals("?.");
+            }
+            // notice multi-level index is not supported, such as[0][0] or 
[0][1]
+            if (part.endsWith("]")) {
+                String num = part.substring(part.lastIndexOf('[') + 1, 
part.length() - 1);
+                if ("last".equals(num)) {
+                    pos = Integer.MAX_VALUE;
+                } else {
+                    pos = Integer.parseInt(num);
+                }
+                part = part.substring(0, part.lastIndexOf('['));
+            }
+            if (answer instanceof JsonObject jo) {
+                answer = pos == -1 ? jo.getMap(part) : jo.getJsonArray(part);
+            } else {
+                answer = pos == -1 ? getMap(part) : getJsonArray(part);
+            }
+            if (pos != -1 && answer instanceof JsonArray arr) {
+                if (pos == Integer.MAX_VALUE) {
+                    answer = arr.getLast();
+                } else if (pos < arr.size()) {
+                    answer = arr.get(pos);
+                } else {
+                    answer = null;
+                }
+            }
+            if (answer == null && !optional) {
+                throw new IllegalArgumentException("JSonObject path " + path + 
" at " + part + " does not exist");
+            } else if (answer == null) {
+                return Optional.empty();
+            }
+        }
+        return Optional.ofNullable(answer);
+    }
+
     /**
      * A convenience method that assumes there is another JsonObject at the 
given key.
      *
diff --git 
a/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonObjectPathTest.java
 
b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonObjectPathTest.java
new file mode 100644
index 000000000000..cea128dbb011
--- /dev/null
+++ 
b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JsonObjectPathTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.util.json;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class JsonObjectPathTest {
+
+    private static final String BOOKS
+            = """
+                    {
+                        "library": {
+                            "book": [
+                                {
+                                    "title": "No Title",
+                                    "author": "F. Scott Fitzgerald",
+                                    "year": "1925",
+                                    "genre": "Classic",
+                                    "movie": false,
+                                    "id": "bk101"
+                                },
+                                {
+                                    "title": "1984",
+                                    "author": "George Orwell",
+                                    "year": "1949",
+                                    "genre": "Dystopian",
+                                    "movie": true,
+                                    "id": "bk102"
+                                }
+                            ]
+                        }
+                    }
+                    """;
+
+    private static final String COUNTRIES
+            = """
+                    {
+                      "countries": [ "Denmark", "Sweden", "Norway" ]
+                    }
+                    """;
+
+    @Test
+    public void testPath() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+
+        JsonObject obj = jo.pathJsonObject("library.book[0]");
+        Assertions.assertNotNull(obj);
+        Assertions.assertEquals("No Title", obj.getString("title"));
+
+        Assertions.assertNull(obj.path("?cheese"));
+        try {
+            Assertions.assertNull(obj.path("cheese"));
+            fail();
+        } catch (Exception e) {
+            // expected
+        }
+
+        obj = jo.pathJsonObject("library.book[1]");
+        Assertions.assertNotNull(obj);
+        Assertions.assertEquals("1984", obj.getString("title"));
+    }
+
+    @Test
+    public void testPathAttribute() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+        Assertions.assertNotNull(jo);
+
+        Assertions.assertEquals("No Title", 
jo.pathString("library.book[0].title"));
+        Assertions.assertEquals("No Title", jo.path("library.book[0].title"));
+        Assertions.assertEquals(1925, jo.pathInteger("library.book[0].year"));
+        Assertions.assertEquals("1925", jo.path("library.book[0].year"));
+        Assertions.assertFalse(jo.pathBoolean("library.book[0].movie"));
+        Assertions.assertEquals(Boolean.FALSE, 
jo.path("library.book[0].movie"));
+        Assertions.assertEquals("1984", 
jo.pathString("library.book[1].title"));
+        Assertions.assertEquals("1984", jo.path("library.book[1].title"));
+        Assertions.assertEquals(1949, jo.pathInteger("library.book[1].year"));
+        Assertions.assertEquals("1949", jo.path("library.book[1].year"));
+        Assertions.assertTrue(jo.pathBoolean("library.book[1].movie"));
+        Assertions.assertEquals(Boolean.TRUE, 
jo.path("library.book[1].movie"));
+
+        try {
+            
Assertions.assertNull(jo.pathJsonObject("library.book[1].unknown"));
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testPathOptional() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+        Assertions.assertNotNull(jo);
+
+        Assertions.assertNotNull(jo.pathJsonObject("library?.book[0]"));
+        Assertions.assertNull(jo.pathJsonObject("library?.book[2]"));
+        try {
+            Assertions.assertNull(jo.pathJsonObject("library.book[2]"));
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testPathAttributeOptional() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+        Assertions.assertNotNull(jo);
+
+        Assertions.assertNull(jo.pathString("library?.book[2]?.subTitle"));
+        Assertions.assertEquals("No Title", 
jo.pathString("library.book[0].title"));
+        Assertions.assertNull(jo.pathString("library.book[0]?.subTitle"));
+        Assertions.assertNull(jo.pathString("?unknown?.book[0].title"));
+
+        Assertions.assertNull(jo.pathString("library?.book[2].title"));
+        Assertions.assertNull(jo.pathInteger("library?.book[2].year"));
+        Assertions.assertNull(jo.pathBoolean("library?.book[2].movie"));
+    }
+
+    @Test
+    public void testPathAttributeArrayLast() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+        Assertions.assertNotNull(jo);
+
+        Assertions.assertEquals("No Title", 
jo.pathString("library.book[0].title"));
+        Assertions.assertEquals(1925, jo.pathInteger("library.book[0].year"));
+        Assertions.assertEquals("1984", 
jo.pathString("library.book[last].title"));
+        Assertions.assertEquals(1949, 
jo.pathInteger("library.book[last].year"));
+    }
+
+    @Test
+    public void testPathAttributeLeafNode() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(BOOKS);
+        Assertions.assertNotNull(jo);
+
+        jo = (JsonObject) 
jo.getJsonObject("library").getJsonArray("book").get(0);
+        Assertions.assertEquals("No Title", jo.pathString("title"));
+        Assertions.assertEquals(1925, jo.pathInteger("year"));
+    }
+
+    @Test
+    public void testPathArray() throws Exception {
+        JsonObject jo = (JsonObject) Jsoner.deserialize(COUNTRIES);
+        Assertions.assertNotNull(jo);
+
+        Assertions.assertEquals("Denmark", jo.path("countries[0]"));
+        Assertions.assertEquals("Sweden", jo.path("countries[1]"));
+        Assertions.assertEquals("Norway", jo.path("countries[2]"));
+        Assertions.assertEquals("Norway", jo.path("countries[last]"));
+        Assertions.assertNull(jo.path("?countries[3]"));
+        try {
+            jo.path("countries[3]");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+}

Reply via email to