org.apache.camel.jsonpath.JsonPathExpression Maven / Gradle / Ivy
/*
* 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.util.Collection;
import java.util.LinkedList;
import java.util.List;
import com.jayway.jsonpath.Option;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExpressionEvaluationException;
import org.apache.camel.ExpressionIllegalSyntaxException;
import org.apache.camel.jsonpath.easypredicate.EasyPredicateParser;
import org.apache.camel.support.ExpressionAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JsonPathExpression extends ExpressionAdapter {
private static final Logger LOG = LoggerFactory.getLogger(JsonPathExpression.class);
private final String expression;
private JsonPathEngine engine;
private boolean predicate;
private Class> resultType;
private boolean suppressExceptions;
private boolean allowSimple = true;
private boolean allowEasyPredicate = true;
private boolean writeAsString;
private boolean unpackArray;
private String headerName;
private String propertyName;
private Option[] options;
public JsonPathExpression(String expression) {
this.expression = expression;
}
public boolean isPredicate() {
return predicate;
}
/**
* Whether to be evaluated as a predicate
*/
public void setPredicate(boolean predicate) {
this.predicate = predicate;
}
public Class> getResultType() {
return resultType;
}
/**
* To configure the result type to use
*/
public void setResultType(Class> resultType) {
this.resultType = resultType;
}
public boolean isSuppressExceptions() {
return suppressExceptions;
}
/**
* Whether to suppress exceptions such as PathNotFoundException
*/
public void setSuppressExceptions(boolean suppressExceptions) {
this.suppressExceptions = suppressExceptions;
}
public boolean isAllowSimple() {
return allowSimple;
}
/**
* Whether to allow in inlined simple exceptions in the json path expression
*/
public void setAllowSimple(boolean allowSimple) {
this.allowSimple = allowSimple;
}
public boolean isAllowEasyPredicate() {
return allowEasyPredicate;
}
/**
* Whether to allow using the easy predicate parser to pre-parse predicates. See {@link EasyPredicateParser} for
* more details.
*/
public void setAllowEasyPredicate(boolean allowEasyPredicate) {
this.allowEasyPredicate = allowEasyPredicate;
}
public boolean isWriteAsString() {
return writeAsString;
}
/**
* Whether to write the output of each row/element as a JSON String value instead of a Map/POJO value.
*/
public void setWriteAsString(boolean writeAsString) {
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;
}
/**
* Name of header to use as input, instead of the message body
*/
public void setHeaderName(String headerName) {
this.headerName = headerName;
}
public String getPropertyName() {
return propertyName;
}
/**
* Name of property to use as input, instead of the message body.
*
* It has a lower precedent than the name of header if both are set.
*/
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public Option[] getOptions() {
return options;
}
/**
* To configure the json path options to use
*/
public void setOptions(Option[] options) {
this.options = options;
}
@Override
public Object evaluate(Exchange exchange) {
try {
Object result = evaluateJsonPath(exchange, engine);
if (resultType != null) {
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 resultTypeIsCollection = Collection.class.isAssignableFrom(resultType);
boolean singleElement = result instanceof List && ((List>) result).size() == 1;
if (singleElement && !resultTypeIsCollection) {
result = ((List>) result).get(0);
LOG.trace("Unwrapping result: {} from single element List before converting to: {}", result,
resultType);
}
} else {
// special for List
boolean resultTypeIsCollection = Collection.class.isAssignableFrom(resultType);
boolean resultIsCollection = result instanceof List;
if (resultTypeIsCollection && !resultIsCollection) {
var list = new LinkedList<>();
list.add(result);
result = list;
}
}
return exchange.getContext().getTypeConverter().convertTo(resultType, exchange, result);
} else {
return result;
}
} catch (Exception e) {
throw new ExpressionEvaluationException(this, exchange, e);
}
}
@Override
public void init(CamelContext context) {
String exp = expression;
if (predicate && isAllowEasyPredicate()) {
EasyPredicateParser parser = new EasyPredicateParser();
exp = parser.parse(expression);
if (!exp.equals(expression)) {
LOG.debug("EasyPredicateParser parsed {} -> {}", expression, exp);
}
}
LOG.debug("Initializing {} using: {}", predicate ? "predicate" : "expression", exp);
try {
engine = new JsonPathEngine(
exp, writeAsString, suppressExceptions, allowSimple, headerName, propertyName, options, context);
} catch (Exception e) {
throw new ExpressionIllegalSyntaxException(exp, e);
}
}
@Override
public String toString() {
return "jsonpath[" + expression + "]";
}
private Object evaluateJsonPath(Exchange exchange, JsonPathEngine engine) throws Exception {
return engine.read(exchange);
}
}