Repository: camel
Updated Branches:
  refs/heads/master 00d1d70ba -> af6889705


CAMEL-11796: Add headerName option to jsonpath expression. Also make jsonpath 
smarter to unwrap single element list result when converting to desired type.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/af688970
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/af688970
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/af688970

Branch: refs/heads/master
Commit: af6889705e63d174e15a03f26fdc658c02871cb0
Parents: 00d1d70
Author: Claus Ibsen <davscl...@apache.org>
Authored: Wed Sep 20 21:04:45 2017 +0200
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Wed Sep 20 21:04:54 2017 +0200

----------------------------------------------------------------------
 .../apache/camel/builder/ExpressionClause.java  |  29 +++
 .../camel/builder/ExpressionClauseSupport.java  |  40 ++++
 .../model/language/JsonPathExpression.java      |  19 ++
 .../src/main/docs/jsonpath-language.adoc        | 204 +++++++++++--------
 .../apache/camel/jsonpath/JsonPathEngine.java   |  45 ++--
 .../camel/jsonpath/JsonPathExpression.java      |  25 ++-
 .../camel/jsonpath/JsonPathHeaderNameTest.java  |  50 +++++
 .../JsonPathTransformHeaderNameTest.java        |  54 +++++
 .../SpringJsonPathTransformHeaderNameTest.java  |  46 +++++
 .../SpringJsonPathTransformHeaderNameTest.xml   |  36 ++++
 .../JsonPathLanguageConfiguration.java          |  12 ++
 11 files changed, 456 insertions(+), 104 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java 
b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
index 9a95020..9e3106a 100644
--- a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
+++ b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClause.java
@@ -499,6 +499,21 @@ public class ExpressionClause<T> extends 
ExpressionDefinition {
     /**
      * Evaluates a <a
      * href="http://camel.apache.org/jsonpath.html";>Json Path
+     * expression</a>
+     *
+     * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as 
PathNotFoundException
+     * @param resultType the return type expected by the expression
+     * @param headerName the name of the header to apply the expression to
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpath(String text, boolean suppressExceptions, Class<?> 
resultType, String headerName) {
+        return delegate.jsonpath(text, suppressExceptions, true, resultType, 
headerName);
+    }
+
+    /**
+     * Evaluates a <a
+     * href="http://camel.apache.org/jsonpath.html";>Json Path
      * expression</a> with writeAsString enabled.
      *
      * @param text the expression to be evaluated
@@ -522,6 +537,20 @@ public class ExpressionClause<T> extends 
ExpressionDefinition {
     }
 
     /**
+     * Evaluates a <a
+     * href="http://camel.apache.org/jsonpath.html";>Json Path
+     * expression</a> with writeAsString enabled.
+     *
+     * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as 
PathNotFoundException
+     * @param headerName the name of the header to apply the expression to
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpathWriteAsString(String text, boolean suppressExceptions, 
String headerName) {
+        return delegate.jsonpathWriteAsString(text, suppressExceptions, true, 
headerName);
+    }
+
+    /**
      * Evaluates a <a href="http://commons.apache.org/jxpath/";>JXPath 
expression</a>
      * 
      * @param text the expression to be evaluated

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
 
b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index c1e487a..91b2d3f 100644
--- 
a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ 
b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -429,6 +429,27 @@ public class ExpressionClauseSupport<T> {
 
     /**
      * Evaluates a <a href="http://camel.apache.org/jsonpath.html";>Json Path
+     * expression</a>
+     *
+     * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as 
PathNotFoundException
+     * @param allowSimple whether to allow in inlined simple exceptions in the 
json path expression
+     * @param resultType the return type expected by the expression
+     * @param headerName the name of the header to apply the expression to
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpath(String text, boolean suppressExceptions, boolean 
allowSimple, Class<?> resultType, String headerName) {
+        JsonPathExpression expression = new JsonPathExpression(text);
+        expression.setSuppressExceptions(suppressExceptions);
+        expression.setAllowSimple(allowSimple);
+        expression.setResultType(resultType);
+        expression.setHeaderName(headerName);
+        setExpressionType(expression);
+        return result;
+    }
+
+    /**
+     * Evaluates a <a href="http://camel.apache.org/jsonpath.html";>Json Path
      * expression</a> with writeAsString enabled.
      *
      * @param text the expression to be evaluated
@@ -471,6 +492,25 @@ public class ExpressionClauseSupport<T> {
     }
 
     /**
+     * Evaluates a <a href="http://camel.apache.org/jsonpath.html";>Json Path
+     * expression</a> with writeAsString enabled.
+     *
+     * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as 
PathNotFoundException
+     * @param allowSimple whether to allow in inlined simple exceptions in the 
json path expression
+     * @param headerName the name of the header to apply the expression to
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpathWriteAsString(String text, boolean suppressExceptions, 
boolean allowSimple, String headerName) {
+        JsonPathExpression expression = new JsonPathExpression(text);
+        expression.setWriteAsString(true);
+        expression.setSuppressExceptions(suppressExceptions);
+        expression.setAllowSimple(allowSimple);
+        expression.setHeaderName(headerName);
+        return expression(expression);
+    }
+
+    /**
      * Evaluates a <a href="http://commons.apache.org/jxpath/";>JXPath 
expression</a>
      *
      * @param text the expression to be evaluated

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
 
b/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
index 574b752..0cf81d7 100644
--- 
a/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
+++ 
b/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
@@ -50,6 +50,8 @@ public class JsonPathExpression extends ExpressionDefinition {
     private Boolean allowEasyPredicate;
     @XmlAttribute @Metadata(defaultValue = "false")
     private Boolean writeAsString;
+    @XmlAttribute
+    private String headerName;
 
     public JsonPathExpression() {
     }
@@ -124,6 +126,17 @@ public class JsonPathExpression extends 
ExpressionDefinition {
         this.writeAsString = writeAsString;
     }
 
+    public String getHeaderName() {
+        return headerName;
+    }
+
+    /**
+     * Name of header to use as input, instead of the message body
+     */
+    public void setHeaderName(String headerName) {
+        this.headerName = headerName;
+    }
+
     public String getLanguage() {
         return "jsonpath";
     }
@@ -157,6 +170,9 @@ public class JsonPathExpression extends 
ExpressionDefinition {
         if (writeAsString != null) {
             setProperty(expression, "writeAsString", writeAsString);
         }
+        if (headerName != null) {
+            setProperty(expression, "headerName", headerName);
+        }
         super.configureExpression(camelContext, expression);
     }
 
@@ -177,6 +193,9 @@ public class JsonPathExpression extends 
ExpressionDefinition {
         if (writeAsString != null) {
             setProperty(predicate, "writeAsString", writeAsString);
         }
+        if (headerName != null) {
+            setProperty(predicate, "headerName", headerName);
+        }
         super.configurePredicate(camelContext, predicate);
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc 
b/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
index 3636a64..9d56b18 100644
--- a/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
+++ b/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
@@ -18,11 +18,11 @@ from("queue:books.new")
       .to("jms:queue:book.expensive")
 -----------------------------------------------------
 
-### JSonPath Options
+=== JSonPath Options
 
 
 // language options: START
-The JSonPath language supports 6 options which are listed below.
+The JSonPath language supports 7 options which are listed below.
 
 
 
@@ -34,82 +34,90 @@ The JSonPath language supports 6 options which are listed 
below.
 | allowSimple | true | Boolean | Whether to allow in inlined simple exceptions 
in the json path expression
 | allowEasyPredicate | true | Boolean | Whether to allow using the easy 
predicate parser to pre-parse predicates.
 | writeAsString | false | Boolean | Whether to write the output of each 
row/element as a JSon String value instead of a Map/POJO value.
+| headerName |  | String | Name of header to use as input instead of the 
message body
 | trim | true | Boolean | Whether to trim the value to remove leading and 
trailing whitespaces and line breaks
 |===
 // language options: END
 
 
 
-### Using XML configuration
+=== Using XML configuration
 
 If you prefer to configure your routes in your link:spring.html[Spring]
 XML file then you can use link:jsonpath.html[JSonPath] expressions as
 follows
 
 [source,xml]
--------------------------------------------------------------------------
-  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
-    <route>
-      <from uri="direct:start"/>
-      <choice>
-        <when>
-          <jsonpath>$.store.book[?(@.price &lt; 10)]</jsonpath>
-          <to uri="mock:cheap"/>
-        </when>
-        <when>
-          <jsonpath>$.store.book[?(@.price &lt; 30)]</jsonpath>
-          <to uri="mock:average"/>
-        </when>
-        <otherwise>
-          <to uri="mock:expensive"/>
-        </otherwise>
-      </choice>
-    </route>
-  </camelContext>
--------------------------------------------------------------------------
-
-### Syntax
+----
+<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
+  <route>
+    <from uri="direct:start"/>
+    <choice>
+      <when>
+        <jsonpath>$.store.book[?(@.price &lt; 10)]</jsonpath>
+        <to uri="mock:cheap"/>
+      </when>
+      <when>
+        <jsonpath>$.store.book[?(@.price &lt; 30)]</jsonpath>
+        <to uri="mock:average"/>
+      </when>
+      <otherwise>
+        <to uri="mock:expensive"/>
+      </otherwise>
+    </choice>
+  </route>
+</camelContext>
+----
+
+=== Syntax
 
 See the https://code.google.com/p/json-path/[JSonPath] project page for
 further examples.
 
-### Easy Syntax
+=== Easy Syntax
 
 *Available as of Camel 2.19*
 
 When you just want to define a basic predicate using jsonpath syntax it can be 
a bit hard to remember the syntax.
  So for example to find out all the cheap books you have to do
 
-    $.store.book[?(@.price < 20)]
+----
+$.store.book[?(@.price < 20)]
+----
 
 However what if you could just write it as
 
-    store.book.price < 20
+----
+store.book.price < 20
+----
 
 And you can omit the path if you just want to look at nodes with a price key
 
-    price < 20
+----
+price < 20
+----
 
 To support this there is a `EasyPredicateParser` which kicks-in if you have 
define the predicate
   using a basic style. That means the predicate must not start with the `$` 
sign, and only include one operator.
 
 The easy syntax is:
 
-   left OP right
+----
+left OP right
+----
 
 You can use Camel simple language in the right operator, eg
 
-    store.book.price < ${header.limit}
+----
+store.book.price < ${header.limit}
+----
 
-
-
-
-### Supported message body types
+=== Supported message body types
 
 Camel JSonPath supports message body using the following types:
 
 [width="100%",cols="3m,7",options="header"]
-|=======================================================================
+|===
 | Type | Comment
 | File | Reading from files
 | String | Plain strings
@@ -119,13 +127,13 @@ Camel JSonPath supports message body using the following 
types:
   is able to use Jackson to read the message body as POJO and convert to 
`java.util.Map`
   which is supported by JSonPath. For example you can add `camel-jackson` as 
dependency to include Jackson.
 | InputStream | If none of the above types matches, then Camel will attempt to 
read the message body as an `java.io.InputStream`.
-|=======================================================================
+|===
 
 If a message body is of unsupported type then an exception is thrown by 
default, however you
 can configure JSonPath to suppress exceptions (see below)
 
 
-### Suppress exceptions
+=== Suppress exceptions
 
 *Available as of Camel 2.16*
 
@@ -136,7 +144,7 @@ contains optional data. Therefore you can set the option
 suppressExceptions to true to ignore this as shown:
 
 [source,java]
----------------------------------------------------
+----
 from("direct:start")
     .choice()
         // use true to suppress exceptions
@@ -144,31 +152,29 @@ from("direct:start")
             .to("mock:middle")
         .otherwise()
             .to("mock:other");
----------------------------------------------------
+----
 
 And in XML DSL:
 
 [source,xml]
---------------------------------------------------------------------------
-    <route>
-      <from uri="direct:start"/>
-      <choice>
-        <when>
-          <jsonpath suppressExceptions="true">person.middlename</jsonpath>
-          <to uri="mock:middle"/>
-        </when>
-        <otherwise>
-          <to uri="mock:other"/>
-        </otherwise>
-      </choice>
-    </route>
---------------------------------------------------------------------------
-
- 
+----
+<route>
+  <from uri="direct:start"/>
+  <choice>
+    <when>
+      <jsonpath suppressExceptions="true">person.middlename</jsonpath>
+      <to uri="mock:middle"/>
+    </when>
+    <otherwise>
+      <to uri="mock:other"/>
+    </otherwise>
+  </choice>
+</route>
+----
 
 This option is also available on the `@JsonPath` annotation.
 
-### Inline Simple exceptions
+=== Inline Simple exceptions
 
 *Available as of Camel 2.18*
 
@@ -176,7 +182,7 @@ Its now possible to inlined Simple language expressions in 
the JSonPath expressi
 An example is shown below:
 
 [source,java]
----------------------------------------------------
+----
 from("direct:start")
   .choice()
     .when().jsonpath("$.store.book[?(@.price < ${header.cheap})]")
@@ -185,12 +191,12 @@ from("direct:start")
       .to("mock:average")
     .otherwise()
       .to("mock:expensive");
----------------------------------------------------
+----
 
 And in XML DSL:
 
 [source,xml]
---------------------------------------------------------------------------
+----
 <route>
   <from uri="direct:start"/>
   <choice>
@@ -207,24 +213,23 @@ And in XML DSL:
     </otherwise>
   </choice>
 </route>
---------------------------------------------------------------------------
+----
 
 You can turn off support for inlined simple expression by setting the option 
allowSimple to false as shown:
 
 [source,java]
----------------------------------------------------
+----
 .when().jsonpath("$.store.book[?(@.price < 10)]", false, false)
----------------------------------------------------
+----
 
 And in XML DSL:
 
 [source,xml]
---------------------------------------------------------------------------
+----
 <jsonpath allowSimple="false">$.store.book[?(@.price < 10)]</jsonpath>
---------------------------------------------------------------------------
+----
 
-
-### JSonPath injection
+=== JSonPath injection
 
 You can use link:bean-integration.html[Bean Integration] to invoke a
 method on a bean and use various languages such as JSonPath to extract a
@@ -233,7 +238,7 @@ value from the message and bind it to a method parameter.
 For example
 
 [source,java]
----------------------------------------------------------------------------------------------------
+----
 public class Foo {
     
     @Consume(uri = "activemq:queue:books.new")
@@ -241,9 +246,9 @@ public class Foo {
       // process the inbound message here
     }
 }
----------------------------------------------------------------------------------------------------
+----
 
-### Encoding Detection
+=== Encoding Detection
 
 *Since Camel version 2.16*, the encoding of the JSON document is
 detected automatically, if the document is encoded in unicode  (UTF-8,
@@ -253,35 +258,66 @@ you enter the document in String format to the JSONPath 
component or you
 can specify the encoding in the header "*CamelJsonPathJsonEncoding*"
 (JsonpathConstants.HEADER_JSON_ENCODING).
 
-### Split JSon data into sub rows as JSon
+=== Split JSon data into sub rows as JSon
 
 You can use jsonpath to split a JSon document, such as:
 
 [source,java]
----------------------------------------------------------------------------------------------------
-    from("direct:start")
-        .split().jsonpath("$.store.book[*]")
-        .to("log:book");
----------------------------------------------------------------------------------------------------
+----
+from("direct:start")
+    .split().jsonpath("$.store.book[*]")
+    .to("log:book");
+----
 
 Then each book is logged, however the message body is a `Map` instance. 
Sometimes
 you may want to output this as plain String JSon value instead, which can be 
done
 from *Camel 2.20* onwards with the `writeAsString` option as shown:
 
 [source,java]
----------------------------------------------------------------------------------------------------
-    from("direct:start")
-        .split().jsonpathWriteAsString("$.store.book[*]")
-        .to("log:book");
----------------------------------------------------------------------------------------------------
+----
+from("direct:start")
+    .split().jsonpathWriteAsString("$.store.book[*]")
+    .to("log:book");
+----
 
 Then each book is logged as a String JSon value. For earlier versions of Camel 
you
 would need to use camel-jackson dataformat and marshal the message body to 
make it
 convert the message body from `Map` to a `String` type.
 
+=== Using header as input
+*Available as of Camel 2.20*
+
+By default jsonpath uses the message body as the input source. However you can 
also use a header as input
+by specifying the `headerName` option.
+
+For example to count the number of books from a json document that
+was stored in a header named `books` you can do:
+
+[source,java]
+----
+from("direct:start")
+    .setHeader("numberOfBooks")
+        .jsonpath("$..store.book.length()", false, int.class, "books")
+    .to("mock:result");
+----
+
+In the `jsonpath` expression above we specify the name of the header as `books`
+and we also told that we wanted the result to be converted as an integer by 
`int.class`.
 
+The same example in XML DSL would be:
+
+[source,xml]
+----
+<route>
+  <from uri="direct:start"/>
+  <setHeader headerName="numberOfBooks">
+    <jsonpath headerName="books" 
resultType="int">$..store.book.length()</jsonpath>
+  </transform>
+  <to uri="mock:result"/>
+</route>
+----
 
-### Dependencies
+=== Dependencies
 
 To use JSonPath in your camel routes you need to add the a dependency on
 *camel-jsonpath* which implements the JSonPath language.
@@ -291,10 +327,10 @@ substituting the version number for the latest & greatest 
release (see
 link:download.html[the download page for the latest versions]).
 
 [source,xml]
------------------------------------------
+----
 <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-jsonpath</artifactId>
   <version>x.x.x</version>
 </dependency>
------------------------------------------
+----

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
 
b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
index d6d506c..d7be041 100644
--- 
a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
+++ 
b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
@@ -52,18 +52,22 @@ public class JsonPathEngine {
     private static final Pattern SIMPLE_PATTERN = 
Pattern.compile("\\$\\{[^\\}]+\\}", Pattern.MULTILINE);
     private final String expression;
     private final boolean writeAsString;
+    private final String headerName;
     private final JsonPath path;
     private final Configuration configuration;
     private JsonPathAdapter adapter;
     private volatile boolean initJsonAdapter;
 
+    @Deprecated
     public JsonPathEngine(String expression) {
-        this(expression, false, false, true, null);
+        this(expression, false, false, true, null, null);
     }
 
-    public JsonPathEngine(String expression, boolean writeAsString, boolean 
suppressExceptions, boolean allowSimple, Option[] options) {
+    public JsonPathEngine(String expression, boolean writeAsString, boolean 
suppressExceptions, boolean allowSimple,
+                          String headerName, Option[] options) {
         this.expression = expression;
         this.writeAsString = writeAsString;
+        this.headerName = headerName;
 
         Defaults defaults = DefaultsImpl.INSTANCE;
         if (options != null) {
@@ -96,6 +100,7 @@ public class JsonPathEngine {
         }
     }
 
+    @SuppressWarnings("unchecked")
     public Object read(Exchange exchange) throws Exception {
         Object answer;
         if (path == null) {
@@ -146,11 +151,9 @@ public class JsonPathEngine {
                 }
                 return map;
             } else {
-                if (adapter != null) {
-                    String json = adapter.writeAsString(answer, exchange);
-                    if (json != null) {
-                        return json;
-                    }
+                String json = adapter.writeAsString(answer, exchange);
+                if (json != null) {
+                    return json;
                 }
             }
         }
@@ -159,12 +162,12 @@ public class JsonPathEngine {
     }
 
     private Object doRead(JsonPath path, Exchange exchange) throws 
IOException, CamelExchangeException {
-        Object json = exchange.getIn().getBody();
+        Object json = headerName != null ? 
exchange.getIn().getHeader(headerName) : exchange.getIn().getBody();
 
         if (json instanceof InputStream) {
             return readWithInputStream(path, exchange);
         } else if (json instanceof GenericFile) {
-            LOG.trace("JSonPath: {} is read as generic file from message body: 
{}", path, json);
+            LOG.trace("JSonPath: {} is read as generic file: {}", path, json);
             GenericFile<?> genericFile = (GenericFile<?>) json;
             if (genericFile.getCharset() != null) {
                 // special treatment for generic file with charset
@@ -174,19 +177,19 @@ public class JsonPathEngine {
         }
 
         if (json instanceof String) {
-            LOG.trace("JSonPath: {} is read as String from message body: {}", 
path, json);
+            LOG.trace("JSonPath: {} is read as String: {}", path, json);
             String str = (String) json;
             return path.read(str, configuration);
         } else if (json instanceof Map) {
-            LOG.trace("JSonPath: {} is read as Map from message body: {}", 
path, json);
+            LOG.trace("JSonPath: {} is read as Map: {}", path, json);
             Map map = (Map) json;
             return path.read(map, configuration);
         } else if (json instanceof List) {
-            LOG.trace("JSonPath: {} is read as List from message body: {}", 
path, json);
+            LOG.trace("JSonPath: {} is read as List: {}", path, json);
             List list = (List) json;
             return path.read(list, configuration);
         } else {
-            // can we find an adapter which can read the message body
+            // can we find an adapter which can read the message body/header
             Object answer = readWithAdapter(path, exchange);
             if (answer == null) {
                 // fallback and attempt input stream for any other types
@@ -207,12 +210,16 @@ public class JsonPathEngine {
         }
 
         // okay it was not then lets throw a failure
-        throw new CamelExchangeException("Cannot read message body as 
supported JSon value", exchange);
+        if (headerName != null) {
+            throw new CamelExchangeException("Cannot read message header " + 
headerName + " as supported JSon value", exchange);
+        } else {
+            throw new CamelExchangeException("Cannot read message body as 
supported JSon value", exchange);
+        }
     }
 
     private Object readWithInputStream(JsonPath path, Exchange exchange) 
throws IOException {
-        Object json = exchange.getIn().getBody();
-        LOG.trace("JSonPath: {} is read as InputStream from message body: {}", 
path, json);
+        Object json = headerName != null ? 
exchange.getIn().getHeader(headerName) : exchange.getIn().getBody();
+        LOG.trace("JSonPath: {} is read as InputStream: {}", path, json);
 
         InputStream is = 
exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class, 
exchange, json);
         if (is != null) {
@@ -232,8 +239,8 @@ public class JsonPathEngine {
     }
 
     private Object readWithAdapter(JsonPath path, Exchange exchange) {
-        Object json = exchange.getIn().getBody();
-        LOG.trace("JSonPath: {} is read with adapter from message body: {}", 
path, json);
+        Object json = headerName != null ? 
exchange.getIn().getHeader(headerName) : exchange.getIn().getBody();
+        LOG.trace("JSonPath: {} is read with adapter: {}", path, json);
 
         doInitAdapter(exchange);
 
@@ -242,7 +249,7 @@ public class JsonPathEngine {
             Map map = adapter.readValue(json, exchange);
             if (map != null) {
                 if (LOG.isDebugEnabled()) {
-                    LOG.debug("JacksonJsonAdapter converted message body from: 
{} to: java.util.Map", ObjectHelper.classCanonicalName(json));
+                    LOG.debug("JacksonJsonAdapter converted object from: {} 
to: java.util.Map", ObjectHelper.classCanonicalName(json));
                 }
                 return path.read(map, configuration);
             }

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
 
b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
index c5bf74f..bbcd69b 100644
--- 
a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
+++ 
b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.jsonpath;
 
+import java.util.Collection;
+import java.util.List;
+
 import com.jayway.jsonpath.Option;
 import org.apache.camel.AfterPropertiesConfigured;
 import org.apache.camel.CamelContext;
@@ -40,6 +43,7 @@ public class JsonPathExpression extends ExpressionAdapter 
implements AfterProper
     private boolean allowSimple = true;
     private boolean allowEasyPredicate = true;
     private boolean writeAsString;
+    private String headerName;
     private Option[] options;
 
     public JsonPathExpression(String expression) {
@@ -113,6 +117,17 @@ public class JsonPathExpression extends ExpressionAdapter 
implements AfterProper
         this.writeAsString = writeAsString;
     }
 
+    public String getHeaderName() {
+        return headerName;
+    }
+
+    /**
+     * Name of header to use as input, instead of the message body
+     */
+    public void setHeaderName(String headerName) {
+        this.headerName = headerName;
+    }
+
     public Option[] getOptions() {
         return options;
     }
@@ -129,6 +144,14 @@ public class JsonPathExpression extends ExpressionAdapter 
implements AfterProper
         try {
             Object result = evaluateJsonPath(exchange, engine);
             if (resultType != null) {
+                // in some cases we get a single element that is wrapped in a 
List, so unwrap that
+                // if we for example want to grab the single entity and 
convert that to a int/boolean/String etc
+                boolean resultIsCollection = 
Collection.class.isAssignableFrom(resultType);
+                boolean singleElement = result instanceof List && ((List) 
result).size() == 1;
+                if (singleElement && !resultIsCollection) {
+                    result = ((List) result).get(0);
+                    LOG.trace("Unwrapping result: {} from single element List 
before converting to: {}", result, resultType);
+                }
                 return 
exchange.getContext().getTypeConverter().convertTo(resultType, exchange, 
result);
             } else {
                 return result;
@@ -157,7 +180,7 @@ public class JsonPathExpression extends ExpressionAdapter 
implements AfterProper
 
         LOG.debug("Initializing {} using: {}", predicate ? "predicate" : 
"expression", exp);
         try {
-            engine = new JsonPathEngine(exp, writeAsString, 
suppressExceptions, allowSimple, options);
+            engine = new JsonPathEngine(exp, writeAsString, 
suppressExceptions, allowSimple, headerName, options);
         } catch (Exception e) {
             throw new ExpressionIllegalSyntaxException(exp, e);
         }

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathHeaderNameTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathHeaderNameTest.java
 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathHeaderNameTest.java
new file mode 100644
index 0000000..5b1ea21
--- /dev/null
+++ 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathHeaderNameTest.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.jsonpath;
+
+import java.io.File;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JsonPathHeaderNameTest extends CamelTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .setHeader("number").jsonpath("$..store.book.length()", 
false, int.class, "myHeader")
+                    .to("mock:result");
+            }
+        };
+    }
+
+    @Test
+    public void testAuthors() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:result").expectedHeaderReceived("number", "2");
+
+        Object file = new File("src/test/resources/books.json");
+        template.sendBodyAndHeader("direct:start", "Hello World", "myHeader", 
file);
+
+        assertMockEndpointsSatisfied();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathTransformHeaderNameTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathTransformHeaderNameTest.java
 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathTransformHeaderNameTest.java
new file mode 100644
index 0000000..1a12d1d
--- /dev/null
+++ 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathTransformHeaderNameTest.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.jsonpath;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JsonPathTransformHeaderNameTest extends CamelTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .transform().jsonpath("$.store.book[*].author", false, 
null, "myHeader")
+                    .to("mock:authors");
+            }
+        };
+    }
+
+    @Test
+    public void testAuthors() throws Exception {
+        getMockEndpoint("mock:authors").expectedMessageCount(1);
+
+        Object file = new File("src/test/resources/books.json");
+        template.sendBodyAndHeader("direct:start", "Hello World", "myHeader", 
file);
+
+        assertMockEndpointsSatisfied();
+
+        List<?> authors = 
getMockEndpoint("mock:authors").getReceivedExchanges().get(0).getIn().getBody(List.class);
+        assertEquals("Nigel Rees", authors.get(0));
+        assertEquals("Evelyn Waugh", authors.get(1));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.java
 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.java
new file mode 100644
index 0000000..2699054
--- /dev/null
+++ 
b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.jsonpath;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.camel.test.spring.CamelSpringTestSupport;
+import org.junit.Test;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class SpringJsonPathTransformHeaderNameTest extends 
CamelSpringTestSupport {
+
+    @Override
+    protected AbstractApplicationContext createApplicationContext() {
+        return new 
ClassPathXmlApplicationContext("org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.xml");
+    }
+
+    @Test
+    public void testAuthors() throws Exception {
+        getMockEndpoint("mock:authors").expectedMessageCount(1);
+
+        template.sendBodyAndHeader("direct:start", "Hello World", "myHeader", 
new File("src/test/resources/books.json"));
+
+        assertMockEndpointsSatisfied();
+
+        List<?> authors = 
getMockEndpoint("mock:authors").getReceivedExchanges().get(0).getIn().getBody(List.class);
+        assertEquals("Nigel Rees", authors.get(0));
+        assertEquals("Evelyn Waugh", authors.get(1));
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/components/camel-jsonpath/src/test/resources/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.xml
----------------------------------------------------------------------
diff --git 
a/components/camel-jsonpath/src/test/resources/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.xml
 
b/components/camel-jsonpath/src/test/resources/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.xml
new file mode 100644
index 0000000..1bac174
--- /dev/null
+++ 
b/components/camel-jsonpath/src/test/resources/org/apache/camel/jsonpath/SpringJsonPathTransformHeaderNameTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+            http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd
+            http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd";>
+
+  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring";>
+    <route>
+      <from uri="direct:start"/>
+      <transform>
+        <jsonpath headerName="myHeader">$.store.book[*].author</jsonpath>
+      </transform>
+      <to uri="mock:authors"/>
+    </route>
+  </camelContext>
+
+</beans>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/af688970/platforms/spring-boot/components-starter/camel-jsonpath-starter/src/main/java/org/apache/camel/jsonpath/springboot/JsonPathLanguageConfiguration.java
----------------------------------------------------------------------
diff --git 
a/platforms/spring-boot/components-starter/camel-jsonpath-starter/src/main/java/org/apache/camel/jsonpath/springboot/JsonPathLanguageConfiguration.java
 
b/platforms/spring-boot/components-starter/camel-jsonpath-starter/src/main/java/org/apache/camel/jsonpath/springboot/JsonPathLanguageConfiguration.java
index 54b098b..fda8cd6 100644
--- 
a/platforms/spring-boot/components-starter/camel-jsonpath-starter/src/main/java/org/apache/camel/jsonpath/springboot/JsonPathLanguageConfiguration.java
+++ 
b/platforms/spring-boot/components-starter/camel-jsonpath-starter/src/main/java/org/apache/camel/jsonpath/springboot/JsonPathLanguageConfiguration.java
@@ -49,6 +49,10 @@ public class JsonPathLanguageConfiguration
      */
     private Boolean writeAsString = false;
     /**
+     * Name of header to use as input instead of the message body
+     */
+    private String headerName;
+    /**
      * Whether to trim the value to remove leading and trailing whitespaces and
      * line breaks
      */
@@ -86,6 +90,14 @@ public class JsonPathLanguageConfiguration
         this.writeAsString = writeAsString;
     }
 
+    public String getHeaderName() {
+        return headerName;
+    }
+
+    public void setHeaderName(String headerName) {
+        this.headerName = headerName;
+    }
+
     public Boolean getTrim() {
         return trim;
     }

Reply via email to