You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mt...@apache.org on 2022/11/02 19:47:46 UTC
[nifi] branch main updated: [NIFI-10612] Initial check in of isJson code. [NIFI-10612] Made suggested change to only test subject value where it is formatted like a Json array or object.
This is an automated email from the ASF dual-hosted git repository.
mthomsen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 2afe2b36b9 [NIFI-10612] Initial check in of isJson code. [NIFI-10612] Made suggested change to only test subject value where it is formatted like a Json array or object.
2afe2b36b9 is described below
commit 2afe2b36b98135689232b6c66e02916dc7e96e05
Author: dan-s1 <ds...@gmail.com>
AuthorDate: Wed Oct 12 20:16:14 2022 +0000
[NIFI-10612] Initial check in of isJson code.
[NIFI-10612] Made suggested change to only test subject value where it is formatted like a Json array or object.
This closes #6574
Signed-off-by: Mike Thomsen <mt...@apache.org>
---
.../language/antlr/AttributeExpressionLexer.g | 1 +
.../language/antlr/AttributeExpressionParser.g | 2 +-
.../language/compile/ExpressionCompiler.java | 5 ++
.../evaluation/functions/IsJsonEvaluator.java | 64 ++++++++++++++++++++++
.../attribute/expression/language/TestQuery.java | 33 +++++++++++
.../main/asciidoc/expression-language-guide.adoc | 33 +++++++++++
6 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
index e0cb4caf24..734794f3be 100644
--- a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
+++ b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionLexer.g
@@ -145,6 +145,7 @@ BASE64_ENCODE : 'base64Encode';
BASE64_DECODE : 'base64Decode';
GET_STATE_VALUE: 'getStateValue';
EVALUATE_EL_STRING: 'evaluateELString';
+IS_JSON: 'isJson';
// 1 arg functions
SUBSTRING_AFTER : 'substringAfter';
diff --git a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
index 997dbd834a..c71719b92c 100644
--- a/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
+++ b/nifi-commons/nifi-expression-language/src/main/antlr3/org/apache/nifi/attribute/expression/language/antlr/AttributeExpressionParser.g
@@ -85,7 +85,7 @@ threeArgString: ((JSON_PATH_PUT) LPAREN! anyArg COMMA! anyArg COMMA! anyArg RPAR
fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg)?)?)?)? RPAREN!;
// functions that return Booleans
-zeroArgBool : (IS_NULL | NOT_NULL | IS_EMPTY | NOT) LPAREN! RPAREN!;
+zeroArgBool : (IS_NULL | NOT_NULL | IS_EMPTY | NOT | IS_JSON) LPAREN! RPAREN!;
oneArgBool : ((FIND | MATCHES | EQUALS_IGNORE_CASE) LPAREN! anyArg RPAREN!) |
(GREATER_THAN | LESS_THAN | GREATER_THAN_OR_EQUAL | LESS_THAN_OR_EQUAL) LPAREN! anyArg RPAREN! |
(EQUALS) LPAREN! anyArg RPAREN! |
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
index 17a0173ec5..05bc23a2ac 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/compile/ExpressionCompiler.java
@@ -68,6 +68,7 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.InEval
import org.apache.nifi.attribute.expression.language.evaluation.functions.IndexOfEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.InstantFormatEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.IsEmptyEvaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.functions.IsJsonEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.IsNullEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathAddEvaluator;
import org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathDeleteEvaluator;
@@ -260,6 +261,7 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.UUID5;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.WHOLE_NUMBER;
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.EVALUATE_EL_STRING;
+import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.IS_JSON;
public class ExpressionCompiler {
private final Set<Evaluator<?>> evaluators = new HashSet<>();
@@ -814,6 +816,9 @@ public class ExpressionCompiler {
}
return addToken(new InEvaluator(toStringEvaluator(subjectEvaluator), list), "in");
}
+ case IS_JSON:
+ verifyArgCount(argEvaluators, 0, "isJson");
+ return addToken(new IsJsonEvaluator(toStringEvaluator(subjectEvaluator)), "isJson");
case FIND: {
verifyArgCount(argEvaluators, 1, "find");
return addToken(new FindEvaluator(toStringEvaluator(subjectEvaluator),
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/IsJsonEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/IsJsonEvaluator.java
new file mode 100644
index 0000000000..4d3815dc26
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/IsJsonEvaluator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.nifi.attribute.expression.language.evaluation.functions;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.attribute.expression.language.EvaluationContext;
+import org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.BooleanQueryResult;
+import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
+import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
+
+import java.io.IOException;
+
+public class IsJsonEvaluator extends BooleanEvaluator {
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+ private final Evaluator<String> subject;
+
+ public IsJsonEvaluator(Evaluator<String> subject) {
+ this.subject = subject;
+ }
+
+ @Override
+ public QueryResult<Boolean> evaluate(EvaluationContext evaluationContext) {
+ final String subjectValue = subject.evaluate(evaluationContext).getValue();
+ if (StringUtils.isNotBlank(subjectValue)
+ && (isPossibleJsonArray(subjectValue) || isPossibleJsonObject(subjectValue))) {
+ try {
+ MAPPER.readTree(subjectValue);
+ return new BooleanQueryResult(true);
+ } catch (IOException ignored) {
+ //IOException ignored
+ }
+ }
+ return new BooleanQueryResult(false);
+ }
+
+ private boolean isPossibleJsonArray(String subject) {
+ return subject.startsWith("[") && subject.endsWith("]");
+ }
+
+ private boolean isPossibleJsonObject(String subject) {
+ return subject.startsWith("{") && subject.endsWith("}");
+ }
+
+ @Override
+ public Evaluator<?> getSubjectEvaluator() {
+ return subject;
+ }
+}
diff --git a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
index 7656548089..4db4f0a943 100644
--- a/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
+++ b/nifi-commons/nifi-expression-language/src/test/java/org/apache/nifi/attribute/expression/language/TestQuery.java
@@ -2365,6 +2365,39 @@ public class TestQuery {
verifyEquals("${myattr0:UUID5(${UUID()}):length()}", attributes, 36L);
}
+ @Test
+ void testIsJson() {
+ final Map<String, String> attributes = new HashMap<>();
+ attributes.put("jsonObj", "{\"name\":\"John\", \"age\":30, \"car\":null}");
+ attributes.put("jsonObjMissingStartingBrace", "\"name\":\"John\", \"age\":30, \"car\":null}");
+ attributes.put("jsonObjMissingEndingBrace", "{\"name\":\"John\", \"age\":30, \"car\":null");
+ attributes.put("jsonArray", "[\"Ford\", \"BMW\", \"Fiat\"]");
+ attributes.put("jsonArrayMissingStartingBracket", "\"Ford\", \"BMW\", \"Fiat\"]");
+ attributes.put("jsonArrayMissingEndingBracket", "[\"Ford\", \"BMW\", \"Fiat\"");
+ attributes.put("emptyQuotedString", "\"\"");
+ attributes.put("quotedString", "\"someString\"");
+ attributes.put("integer", "1234");
+ attributes.put("decimal", "18.36");
+ attributes.put("trueAttr", "true");
+ attributes.put("falseAttr", "false");
+ attributes.put("nullAttr", "null");
+
+ verifyEquals("${jsonObj:isJson()}", attributes, true);
+ verifyEquals("${jsonObjMissingStartingBrace:isJson()}", attributes, false);
+ verifyEquals("${jsonObjMissingEndingBrace:isJson()}", attributes, false);
+ verifyEquals("${jsonArray:isJson()}", attributes, true);
+ verifyEquals("${jsonArrayMissingStartingBracket:isJson()}", attributes, false);
+ verifyEquals("${jsonArrayMissingEndingBracket:isJson()}", attributes, false);
+ verifyEquals("${someAttribute:isJson()}", attributes, false);
+ verifyEquals("${emptyQuotedString:isJson()}", attributes, false);
+ verifyEquals("${quotedString:isJson()}", attributes, false);
+ verifyEquals("${integer:isJson()}", attributes, false);
+ verifyEquals("${decimal:isJson()}", attributes, false);
+ verifyEquals("${trueAttr:isJson()}", attributes, false);
+ verifyEquals("${falseAttr:isJson()}", attributes, false);
+ verifyEquals("${nullAttr:isJson()}", attributes, false);
+ }
+
private void verifyEquals(final String expression, final Map<String, String> attributes, final Object expectedResult) {
verifyEquals(expression,attributes, null, ParameterLookup.EMPTY, expectedResult);
}
diff --git a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
index 173100efbf..8fab2761b1 100644
--- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc
@@ -569,6 +569,39 @@ the following results:
| `${filename:isNull():not():ifElse('found', 'not_found')}` | `found`
|===================================================================
+[.function]
+=== isJson
+*Description*: [.description]#The `isJson` function returns `true` if the subject is a JSON array or a JSON object, `false` otherwise. This is typically used to determine whether an attribute is JSON in order to allow for a follow-on JSONPath query. Although technically there are other valid JSON types such as string, number, boolean and null, this method is only concerned with the primary JSON objects queried with JSONPath , arrays and objects. #
+
+*Subject Type*: [.subject]#Any#
+
+*Arguments*: No arguments
+
+*Return Type*: [.returnType]#Boolean#
+
+*Examples*: If the attribute "jsonObj" has the value {"name":"John", "age":30, "car":null}, the attribute jsonObjMissingStartingBrace has the value "name":"John", "age":30, "car":null}, the attribute jsonObjMissingEndingBrace has the value {"name":"John", "age":30, "car":null, the attribute "jsonArray" has the value ["Ford", "BMW", "Fiat"], the attribute jsonArrayMissingStartingBracket has the value "Ford", "BMW", "Fiat"], the attribute jsonArrayMissingEndingBracket has the value ["Ford" [...]
+
+
+
+.isJson Examples
+|===================================================================
+| Expression | Value
+| `${jsonObj:isJson()}` | `true`
+| `${jsonObjMissingStartingBrace:isJson()}` | `false`
+| `${jsonObjMissingEndingBrace:isJson()}` | `false`
+| `${jsonArray:isJson()}` | `true`
+| `${jsonArrayMissingStartingBracket:isJson()}` | `false`
+| `${jsonArrayMissingEndingBracket:isJson()}` | `false`
+| `${someAttribute:isJson()}` | `false`
+| `${emptyQuotedString:isJson())` | `false`
+| `${quotedString:isJson()}` | `false`
+| `${integer:isJson()}` | `false`
+| `${decimal:isJson()}` | `false`
+| `${trueAttr:isJson()}` | `false`
+| `${falseAttr:isJson()}` | `false`
+| `${nullAttr:isJson()}` | `false`
+|===================================================================
+