You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/10/19 17:56:00 UTC
[camel] 01/02: [CAMEL-18612] Inconsistency in JsonPath component causes problems with databinding (#8571)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-3.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 298f1e0a1fe46832d563cb125e7b961bccf64b0f
Author: Radovan Netuka <rn...@redhat.com>
AuthorDate: Wed Oct 19 18:19:10 2022 +0200
[CAMEL-18612] Inconsistency in JsonPath component causes problems with databinding (#8571)
---
.../src/main/docs/jsonpath-language.adoc | 13 +++++++++
.../apache/camel/jsonpath/JsonPathExpression.java | 28 ++++++++++++++-----
.../apache/camel/jsonpath/JsonPathLanguage.java | 19 +++++++++++--
.../camel/jsonpath/JsonPathLanguageTest.java | 32 ++++++++++++++++++++++
.../org/apache/camel/builder/ExpressionClause.java | 12 ++++++++
.../camel/builder/ExpressionClauseSupport.java | 15 ++++++++++
.../camel/model/language/JsonPathExpression.java | 14 ++++++++++
.../language/JsonPathExpressionReifier.java | 7 +++--
8 files changed, 128 insertions(+), 12 deletions(-)
diff --git a/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc b/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
index 9243f364659..1e52cc68582 100644
--- a/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
+++ b/components/camel-jsonpath/src/main/docs/jsonpath-language.adoc
@@ -272,6 +272,19 @@ from("direct:start")
Then each book is logged as a String JSON value.
+== Unpack a single-element array into an object
+
+It is possible to unpack a single-element array into an object:
+
+[source,java]
+----
+from("direct:start")
+ .setBody().jsonpathUnpack("$.store.book", Book.class)
+ .to("log:book");
+----
+
+If book array contains only one book, it will be converted into a Book object.
+
== Using header as input
By default, JSONPath uses the message body as the input source. However, you can also use a header as input
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 c2cb7df322a..cb595abccc4 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
@@ -42,6 +42,7 @@ public class JsonPathExpression extends ExpressionAdapter {
private boolean allowSimple = true;
private boolean allowEasyPredicate = true;
private boolean writeAsString;
+ private boolean unpackArray;
private String headerName;
private Option[] options;
@@ -116,6 +117,17 @@ public class JsonPathExpression extends ExpressionAdapter {
this.writeAsString = writeAsString;
}
+ public boolean isUnpackArray() {
+ return unpackArray;
+ }
+
+ /**
+ * Whether to unpack a single element json-array into an object.
+ */
+ public void setUnpackArray(boolean unpackArray) {
+ this.unpackArray = unpackArray;
+ }
+
public String getHeaderName() {
return headerName;
}
@@ -143,13 +155,15 @@ public class JsonPathExpression extends ExpressionAdapter {
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);
+ if (unpackArray) {
+ // 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 {
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
index 5b79903c03e..650fd912ef6 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java
@@ -38,6 +38,7 @@ public class JsonPathLanguage extends LanguageSupport implements PropertyConfigu
private boolean allowSimple = true;
private boolean allowEasyPredicate = true;
private boolean writeAsString;
+ private boolean unpackArray;
private String headerName;
private Option[] options;
@@ -81,6 +82,14 @@ public class JsonPathLanguage extends LanguageSupport implements PropertyConfigu
this.writeAsString = writeAsString;
}
+ public boolean isUnpackArray() {
+ return unpackArray;
+ }
+
+ public void setUnpackArray(boolean unpackArray) {
+ this.unpackArray = unpackArray;
+ }
+
public String getHeaderName() {
return headerName;
}
@@ -113,6 +122,7 @@ public class JsonPathLanguage extends LanguageSupport implements PropertyConfigu
answer.setAllowEasyPredicate(allowEasyPredicate);
answer.setHeaderName(headerName);
answer.setWriteAsString(writeAsString);
+ answer.setUnpackArray(unpackArray);
answer.setHeaderName(headerName);
answer.setOptions(options);
answer.init(getCamelContext());
@@ -134,8 +144,9 @@ public class JsonPathLanguage extends LanguageSupport implements PropertyConfigu
answer.setAllowSimple(property(boolean.class, properties, 2, allowSimple));
answer.setAllowEasyPredicate(property(boolean.class, properties, 3, allowEasyPredicate));
answer.setWriteAsString(property(boolean.class, properties, 4, writeAsString));
- answer.setHeaderName(property(String.class, properties, 5, headerName));
- String option = (String) properties[6];
+ answer.setUnpackArray(property(boolean.class, properties, 5, unpackArray));
+ answer.setHeaderName(property(String.class, properties, 6, headerName));
+ String option = (String) properties[7];
if (option != null) {
List<Option> list = new ArrayList<>();
for (String s : option.split(",")) {
@@ -192,6 +203,10 @@ public class JsonPathLanguage extends LanguageSupport implements PropertyConfigu
case "writeAsString":
setWriteAsString(PropertyConfigurerSupport.property(camelContext, boolean.class, value));
return true;
+ case "unpackarray":
+ case "unpackArray":
+ setUnpackArray(PropertyConfigurerSupport.property(camelContext, boolean.class, value));
+ return true;
case "options":
setOptions(PropertyConfigurerSupport.property(camelContext, Option[].class, value));
return true;
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathLanguageTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathLanguageTest.java
index 8b9f068f64f..37cae41c8fa 100644
--- a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathLanguageTest.java
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathLanguageTest.java
@@ -137,4 +137,36 @@ public class JsonPathLanguageTest extends CamelTestSupport {
assertNull(nofoo);
}
+ @Test
+ public void testUnpackJsonArray() {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(new File("src/test/resources/expensive.json"));
+
+ JsonPathLanguage language = (JsonPathLanguage) context.resolveLanguage("jsonpath");
+ language.setUnpackArray(true);
+ language.setResultType(String.class);
+
+ JsonPathExpression expression = (JsonPathExpression) language.createExpression("$.store.book");
+ String json = (String) expression.evaluate(exchange);
+
+ // check that a single json object is returned, not an array
+ assertTrue(json.startsWith("{") && json.endsWith("}"));
+ }
+
+ @Test
+ public void testDontUnpackJsonArray() {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(new File("src/test/resources/expensive.json"));
+
+ JsonPathLanguage language = (JsonPathLanguage) context.resolveLanguage("jsonpath");
+ language.setUnpackArray(false);
+ language.setResultType(String.class);
+
+ JsonPathExpression expression = (JsonPathExpression) language.createExpression("$.store.book");
+ String json = (String) expression.evaluate(exchange);
+
+ // check that an array is returned, not a single object
+ assertTrue(json.startsWith("[") && json.endsWith("]"));
+ }
+
}
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
index d785fe9a27c..309c6b75233 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClause.java
@@ -570,6 +570,18 @@ public class ExpressionClause<T> implements Expression, Predicate {
return delegate.jsonpathWriteAsString(text, suppressExceptions, true, headerName);
}
+ /**
+ * Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path expression</a> with unpacking a
+ * single-element array into an object enabled.
+ *
+ * @param text the expression to be evaluated
+ * @param resultType the return type expected by the expression
+ * @return the builder to continue processing the DSL
+ */
+ public T jsonpathUnpack(String text, Class<?> resultType) {
+ return delegate.jsonpathUnpack(text, resultType);
+ }
+
/**
* Evaluates an <a href="http://camel.apache.org/ognl.html">OGNL expression</a>
*
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index fba2d270aa6..8192c1d09f6 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -679,6 +679,21 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware, Predi
return expression(expression);
}
+ /**
+ * Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path expression</a> with unpacking a
+ * single-element array into an object enabled.
+ *
+ * @param text the expression to be evaluated
+ * @param resultType the return type expected by the expression
+ * @return the builder to continue processing the DSL
+ */
+ public T jsonpathUnpack(String text, Class<?> resultType) {
+ JsonPathExpression expression = new JsonPathExpression(text);
+ expression.setUnpackArray(Boolean.toString(true));
+ expression.setResultType(resultType);
+ return expression(expression);
+ }
+
/**
* Evaluates an <a href="http://camel.apache.org/ognl.html">OGNL expression</a>
*
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/language/JsonPathExpression.java b/core/camel-core-model/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
index bae9b501d86..5a675b3b455 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
@@ -49,6 +49,9 @@ public class JsonPathExpression extends ExpressionDefinition {
@Metadata(defaultValue = "false", javaType = "java.lang.Boolean")
private String writeAsString;
@XmlAttribute
+ @Metadata(defaultValue = "false", javaType = "java.lang.Boolean")
+ private String unpackArray;
+ @XmlAttribute
@Metadata(label = "advanced")
private String headerName;
@XmlAttribute
@@ -129,6 +132,17 @@ public class JsonPathExpression extends ExpressionDefinition {
this.writeAsString = writeAsString;
}
+ public String getUnpackArray() {
+ return unpackArray;
+ }
+
+ /**
+ * Whether to unpack a single element json-array into an object.
+ */
+ public void setUnpackArray(String unpackArray) {
+ this.unpackArray = unpackArray;
+ }
+
public String getHeaderName() {
return headerName;
}
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/JsonPathExpressionReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/JsonPathExpressionReifier.java
index 5ef7d1eaec5..b3de1829766 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/JsonPathExpressionReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/language/JsonPathExpressionReifier.java
@@ -43,14 +43,15 @@ public class JsonPathExpressionReifier extends ExpressionReifier<JsonPathExpress
}
private Object[] createProperties() {
- Object[] properties = new Object[7];
+ Object[] properties = new Object[8];
properties[0] = definition.getResultType();
properties[1] = parseBoolean(definition.getSuppressExceptions());
properties[2] = parseBoolean(definition.getAllowSimple());
properties[3] = parseBoolean(definition.getAllowEasyPredicate());
properties[4] = parseBoolean(definition.getWriteAsString());
- properties[5] = parseString(definition.getHeaderName());
- properties[6] = parseString(definition.getOption());
+ properties[5] = parseBoolean(definition.getUnpackArray());
+ properties[6] = parseString(definition.getHeaderName());
+ properties[7] = parseString(definition.getOption());
return properties;
}