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 fb856b09dc..0f93016acb 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 @@ -188,6 +188,7 @@ REPLACE : 'replace'; REPLACE_FIRST : 'replaceFirst'; REPLACE_ALL : 'replaceAll'; IF_ELSE : 'ifElse'; +JSON_PATH_SET : 'jsonPathSet'; PAD_LEFT : 'padLeft'; PAD_RIGHT : 'padRight'; 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 15f68e5d9a..30576b714f 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 @@ -78,7 +78,7 @@ zeroArgString : (TO_UPPER | TO_LOWER | TRIM | TO_STRING | URL_ENCODE | URL_DECOD oneArgString : ((SUBSTRING_BEFORE | SUBSTRING_BEFORE_LAST | SUBSTRING_AFTER | SUBSTRING_AFTER_LAST | REPLACE_NULL | REPLACE_EMPTY | PREPEND | APPEND | STARTS_WITH | ENDS_WITH | CONTAINS | JOIN | JSON_PATH | JSON_PATH_DELETE | FROM_RADIX) LPAREN! anyArg RPAREN!) | (TO_RADIX LPAREN! anyArg (COMMA! anyArg)? RPAREN!); -twoArgString : ((REPLACE | REPLACE_FIRST | REPLACE_ALL | IF_ELSE) LPAREN! anyArg COMMA! anyArg RPAREN!) | +twoArgString : ((REPLACE | REPLACE_FIRST | REPLACE_ALL | IF_ELSE | JSON_PATH_SET) LPAREN! anyArg COMMA! anyArg RPAREN!) | ((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!); fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! 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 63e3ba4b3a..20b10d0b9d 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 @@ -29,8 +29,11 @@ import org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionLe import org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser; import org.apache.nifi.attribute.expression.language.evaluation.BooleanEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.DateEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.DecimalEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.Evaluator; +import org.apache.nifi.attribute.expression.language.evaluation.NumberEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.WholeNumberEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.cast.BooleanCastEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.cast.DateCastEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.cast.DecimalCastEvaluator; @@ -63,6 +66,7 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.IsEmpt import org.apache.nifi.attribute.expression.language.evaluation.functions.IsNullEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathDeleteEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathEvaluator; +import org.apache.nifi.attribute.expression.language.evaluation.functions.JsonPathSetEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.functions.LastIndexOfEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.functions.LengthEvaluator; import org.apache.nifi.attribute.expression.language.evaluation.functions.LessThanEvaluator; @@ -177,6 +181,7 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JOIN; import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JSON_PATH; import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JSON_PATH_DELETE; +import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.JSON_PATH_SET; import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LAST_INDEX_OF; import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LENGTH; import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.LESS_THAN; @@ -945,6 +950,29 @@ public class ExpressionCompiler { return addToken(new JsonPathDeleteEvaluator(toStringEvaluator(subjectEvaluator), toStringEvaluator(argEvaluators.get(0), "first argument to jsonPathDelete")), "jsonPathDelete"); } + case JSON_PATH_SET: { + verifyArgCount(argEvaluators, 2, "jsonPathSet"); + Evaluator valueEvaluator = null; + Evaluator argValueEvaluator = argEvaluators.get(1); + String location = "second argument to jsonPathSet"; + if (argValueEvaluator instanceof StringEvaluator) { + valueEvaluator = toStringEvaluator(argValueEvaluator, location); + } else if (argValueEvaluator instanceof DecimalEvaluator) { + valueEvaluator = toDecimalEvaluator(argValueEvaluator, location); + } else if (argValueEvaluator instanceof NumberEvaluator) { + valueEvaluator = toNumberEvaluator(argValueEvaluator, location); + } else if (argValueEvaluator instanceof WholeNumberEvaluator) { + valueEvaluator = toWholeNumberEvaluator(argValueEvaluator, location); + } else if (argValueEvaluator instanceof BooleanEvaluator) { + valueEvaluator = toBooleanEvaluator(argValueEvaluator, location); + } else { + throw new AttributeExpressionLanguageParsingException("Cannot implicitly convert Data Type " + + argValueEvaluator.getResultType() + (location == null ? "" : " at location [" + location + "]")); + } + return addToken(new JsonPathSetEvaluator(toStringEvaluator(subjectEvaluator), + toStringEvaluator(argEvaluators.get(0), "first argument to jsonPathSet"), + valueEvaluator), "jsonPathSet"); + } case IF_ELSE: { verifyArgCount(argEvaluators, 2, "ifElse"); return addToken(new IfElseEvaluator(toBooleanEvaluator(subjectEvaluator), diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathEvaluator.java index c12a807d31..c496285694 100644 --- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathEvaluator.java +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathEvaluator.java @@ -23,12 +23,16 @@ import org.apache.nifi.attribute.expression.language.evaluation.StringQueryResul import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * JsonPathEvaluator provides access to document at the specified JsonPath */ public class JsonPathEvaluator extends JsonPathBaseEvaluator { + private static final Logger LOGGER = LoggerFactory.getLogger(JsonPathEvaluator.class); + public JsonPathEvaluator(final Evaluator subject, final Evaluator jsonPathExp) { super(subject, jsonPathExp); } @@ -43,7 +47,7 @@ public class JsonPathEvaluator extends JsonPathBaseEvaluator { try { result = documentContext.read(compiledJsonPath); } catch (Exception e) { - // assume the path did not match anything in the document + LOGGER.error("Exception while reading JsonPath " + compiledJsonPath.getPath(), e); return EMPTY_RESULT; } diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathSetEvaluator.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathSetEvaluator.java new file mode 100644 index 0000000000..a7aead0f4f --- /dev/null +++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/evaluation/functions/JsonPathSetEvaluator.java @@ -0,0 +1,57 @@ +/* + * 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.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import org.apache.nifi.attribute.expression.language.EvaluationContext; +import org.apache.nifi.attribute.expression.language.evaluation.Evaluator; +import org.apache.nifi.attribute.expression.language.evaluation.QueryResult; +import org.apache.nifi.attribute.expression.language.evaluation.StringQueryResult; + +/** + * JsonPathSetEvaluator allows setting values at the specified existing path + */ +public class JsonPathSetEvaluator extends JsonPathBaseEvaluator { + + protected Evaluator valueEvaluator; + + public JsonPathSetEvaluator(final Evaluator subject, final Evaluator jsonPathExp, final Evaluator valueEvaluator) { + super(subject, jsonPathExp); + this.valueEvaluator = valueEvaluator; + } + + @Override + public QueryResult evaluate(EvaluationContext context) { + DocumentContext documentContext = getDocumentContext(context); + final JsonPath compiledJsonPath = getJsonPath(context); + + final Object value = valueEvaluator.evaluate(context).getValue(); + + String result; + try { + result = documentContext.set(compiledJsonPath, value).jsonString(); + } catch (Exception e) { + // assume the path did not match anything in the document + return EMPTY_RESULT; + } + + return new StringQueryResult(getResultRepresentation(result, EMPTY_RESULT.getValue())); + } + +} + 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 d971807550..c45f7f56b9 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 @@ -38,6 +38,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -60,6 +61,27 @@ import static org.junit.Assert.fail; public class TestQuery { + // Address book JsonPath constants + public static final String ADDRESS_BOOK_JSON_PATH_FIRST_NAME = "${json:jsonPath('$.firstName')}"; + public static final String ADDRESS_BOOK_JSON_PATH_LAST_NAME = "${json:jsonPath('$.lastName')}"; + public static final String ADDRESS_BOOK_JSON_PATH_AGE = "${json:jsonPath('$.age')}"; + public static final String ADDRESS_BOOK_JSON_PATH_VOTER = "${json:jsonPath('$.voter')}"; + public static final String ADDRESS_BOOK_JSON_PATH_ADDRESS_POSTAL_CODE = "${json:jsonPath('$.address.postalCode')}"; + public static final String ADDRESS_BOOK_JSON_PATH_PHONE_NUMBERS_TYPE_HOME_NUMBER = "${json:jsonPath(\"$.phoneNumbers[?(@.type=='home')].number\")}"; + public static final String ADDRESS_BOOK_JSON_PATH_PHONE_NUMBERS_TYPE_OFFICE_NUMBER = "${json:jsonPath(\"$.phoneNumbers[?(@.type=='office')].number\")}"; + public static final String ADDRESS_BOOK_JSON_PATH_HEIGHT = "${json:jsonPath('$.height')}"; + public static final String ADDRESS_BOOK_JSON_PATH_EMPTY = ""; + + private static final List phoneBookAttributes = Arrays.asList( + ADDRESS_BOOK_JSON_PATH_FIRST_NAME, + ADDRESS_BOOK_JSON_PATH_LAST_NAME, + ADDRESS_BOOK_JSON_PATH_AGE, + ADDRESS_BOOK_JSON_PATH_VOTER, + ADDRESS_BOOK_JSON_PATH_ADDRESS_POSTAL_CODE, + ADDRESS_BOOK_JSON_PATH_PHONE_NUMBERS_TYPE_HOME_NUMBER, + ADDRESS_BOOK_JSON_PATH_PHONE_NUMBERS_TYPE_OFFICE_NUMBER + ); + @Test public void testCompilation() { assertInvalid("${attr:uuid()}"); @@ -298,17 +320,12 @@ public class TestQuery { verifyEquals("${#{test}:append(' - '):append(#{test})}", attributes, stateValues, parameters,"unit - unit"); } - @Test public void testJsonPath() throws IOException { - final Map attributes = new HashMap<>(); - attributes.put("json", getResourceAsString("/json/address-book.json")); - verifyEquals("${json:jsonPath('$.firstName')}", attributes, "John"); - verifyEquals("${json:jsonPath('$.address.postalCode')}", attributes, "10021-3100"); - verifyEquals("${json:jsonPath(\"$.phoneNumbers[?(@.type=='home')].number\")}", attributes, "212 555-1234"); - verifyEquals("${json:jsonPath('$.phoneNumbers')}", attributes, - "[{\"type\":\"home\",\"number\":\"212 555-1234\"},{\"type\":\"office\",\"number\":\"646 555-4567\"}]"); - verifyEquals("${json:jsonPath('$.missing-path')}", attributes, ""); + Map attributes = verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_EMPTY, + "", "${json:jsonPathDelete('$.missingpath')}", ""); + verifyEquals("${json:jsonPath('$.missingpath')}", attributes, ""); try { verifyEquals("${json:jsonPath('$..')}", attributes, ""); Assert.fail("Did not detect bad JSON path expression"); @@ -327,67 +344,112 @@ public class TestQuery { } } - @Test - public void testJsonPathDeleteFirstNameAttribute() throws IOException { + private void verifyAddressBookAttributes(String originalAddressBook, Map attributes, String updatedAttribute, Object updatedValue) { + + Map originalAttributes = new HashMap<>(); + originalAttributes.put("json", originalAddressBook); + + phoneBookAttributes.stream() + .filter(currentAttribute -> !currentAttribute.equals(updatedAttribute)) + .forEach(currentAttribute -> { + String expected = Query.evaluateExpressions(currentAttribute, originalAttributes, null, null, ParameterLookup.EMPTY); + verifyEquals(currentAttribute, attributes, expected); + } + ); + if (! ADDRESS_BOOK_JSON_PATH_EMPTY.equals(updatedAttribute) ) { + verifyEquals(updatedAttribute, attributes, updatedValue); + } + } + + private Map verifyJsonPathExpressions(String targetAttribute, Object originalValue, String updateExpression, Object updatedValue) throws IOException { final Map attributes = new HashMap<>(); String addressBook = getResourceAsString("/json/address-book.json"); attributes.put("json", addressBook); - verifyEquals("${json:jsonPath('$.firstName')}", attributes, "John"); + if ( ! ADDRESS_BOOK_JSON_PATH_EMPTY.equals(targetAttribute) ) { + verifyEquals(targetAttribute, attributes, originalValue); + } - String addressBookAfterDelete = Query.evaluateExpressions("${json:jsonPathDelete('$.firstName')}", attributes, ParameterLookup.EMPTY); + String addressBookAfterDelete = Query.evaluateExpressions(updateExpression, attributes, ParameterLookup.EMPTY); attributes.clear(); attributes.put("json", addressBookAfterDelete); - verifyEquals("${json:jsonPath('$.lastName')}", attributes, "Smith"); - verifyEquals("${json:jsonPath('$.age')}", attributes, "25"); - verifyEquals("${json:jsonPath('$.address.postalCode')}", attributes, "10021-3100"); - verifyEquals("${json:jsonPath(\"$.phoneNumbers[?(@.type=='home')].number\")}", attributes, "212 555-1234"); - verifyEquals("${json:jsonPath('$.phoneNumbers')}", attributes, - "[{\"type\":\"home\",\"number\":\"212 555-1234\"},{\"type\":\"office\",\"number\":\"646 555-4567\"}]"); + verifyAddressBookAttributes(addressBook, attributes, targetAttribute, updatedValue); - verifyEquals("${json:jsonPath('$.firstName')}", attributes, ""); + return attributes; + } + + @Test + public void testJsonPathDeleteFirstNameAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_FIRST_NAME, + "John", + "${json:jsonPathDelete('$.firstName')}", + "" + ); } @Test public void testJsonPathDeleteMissingPath() throws IOException { - final Map attributes = new HashMap<>(); - String addressBook = getResourceAsString("/json/address-book.json"); - attributes.put("json", addressBook); - - String addressBookAfterDelete = Query.evaluateExpressions("${json:jsonPathDelete('$.missing-path')}", attributes, ParameterLookup.EMPTY); - attributes.clear(); - attributes.put("json", addressBookAfterDelete); - - verifyEquals("${json:jsonPath('$.firstName')}", attributes, "John"); - verifyEquals("${json:jsonPath('$.lastName')}", attributes, "Smith"); - verifyEquals("${json:jsonPath('$.age')}", attributes, "25"); - verifyEquals("${json:jsonPath('$.address')}", attributes, - "{\"streetAddress\":\"21 2nd Street\",\"city\":\"New York\",\"state\":\"NY\",\"postalCode\":\"10021-3100\"}"); - verifyEquals("${json:jsonPath('$.phoneNumbers')}", attributes, - "[{\"type\":\"home\",\"number\":\"212 555-1234\"},{\"type\":\"office\",\"number\":\"646 555-4567\"}]"); + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_EMPTY, + "", + "${json:jsonPathDelete('$.missing-path')}", + ""); } @Test public void testJsonPathDeleteHomePhoneNumber() throws IOException { - final Map attributes = new HashMap<>(); - String addressBook = getResourceAsString("/json/address-book.json"); - attributes.put("json", addressBook); + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_PHONE_NUMBERS_TYPE_HOME_NUMBER, + "212 555-1234", + "${json:jsonPathDelete(\"$.phoneNumbers[?(@.type=='home')]\")}", + "[]"); + } - verifyEquals("${json:jsonPath(\"$.phoneNumbers[?(@.type=='home')].number\")}", attributes, "212 555-1234"); + @Test + public void testJsonPathSetFirstNameAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_FIRST_NAME, + "John", + "${json:jsonPathSet('$.firstName', 'James')}", + "James"); + } - String addressBookAfterDelete = Query.evaluateExpressions("${json:jsonPathDelete(\"$.phoneNumbers[?(@.type=='home')]\")}", attributes, ParameterLookup.EMPTY); + @Test + public void testJsonPathSetAgeWholeNumberAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_AGE, + "25", + "${json:jsonPathSet('$.age', '35')}", + "35"); + } - attributes.clear(); - attributes.put("json", addressBookAfterDelete); + @Test + public void testJsonPathSetVoterBooleanAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_VOTER, + "true", + "${json:jsonPathSet('$.voter', false)}", + "false"); + } - verifyEquals("${json:jsonPath('$.firstName')}", attributes, "John"); - verifyEquals("${json:jsonPath('$.lastName')}", attributes, "Smith"); - verifyEquals("${json:jsonPath('$.age')}", attributes, "25"); - verifyEquals("${json:jsonPath('$.address.postalCode')}", attributes, "10021-3100"); - verifyEquals("${json:jsonPath(\"$.phoneNumbers[?(@.type=='home')].number\")}", attributes, "[]"); - verifyEquals("${json:jsonPath('$.phoneNumbers')}", attributes, - "{\"type\":\"office\",\"number\":\"646 555-4567\"}"); + @Test + public void testJsonPathSetHeightNumberAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_HEIGHT, + "6.1", + "${json:jsonPathSet('$.height', 5.9)}", + "5.9"); + } + + @Test + public void testJsonPathSetMissingPathAttribute() throws IOException { + verifyJsonPathExpressions( + ADDRESS_BOOK_JSON_PATH_EMPTY, + "", + "${json:jsonPathSet('$.missing-path', 5.9)}", + ""); } @Test diff --git a/nifi-commons/nifi-expression-language/src/test/resources/json/address-book.json b/nifi-commons/nifi-expression-language/src/test/resources/json/address-book.json index 3348bc36fb..3f7981beed 100644 --- a/nifi-commons/nifi-expression-language/src/test/resources/json/address-book.json +++ b/nifi-commons/nifi-expression-language/src/test/resources/json/address-book.json @@ -1,5 +1,9 @@ { - "firstName": "John", "lastName": "Smith", "age": 25, + "firstName": "John", + "lastName": "Smith", + "age": 25, + "voter" : true, + "height" : 6.1, "address" : { "streetAddress": "21 2nd Street", "city": "New York", diff --git a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc index c6949c4932..e85ed5d411 100644 --- a/nifi-docs/src/main/asciidoc/expression-language-guide.adoc +++ b/nifi-docs/src/main/asciidoc/expression-language-guide.adoc @@ -1698,6 +1698,58 @@ form of the updated JSON.# An empty subject value or a subject value with an invalid JSON document results in an exception bulletin. +[.function] +=== jsonPathSet + +*Description*: [.description]#The `jsonPathSet` function sets the value at the specified JsonPath on a Subject JSON and returns string +form of the updated JSON.# + +*Subject Type*: [.subject]#String# + +*Arguments*: + + - [.argName]#_jsonPath_# : [.argDesc]#the JSON path expression to set value on the Subject.# + - [.argName]#_value_# : [.argDesc]#the value expression to be set on the specified path on Subject.# + +*Return Type*: [.returnType]#String# + +*Examples*: If the "myJson" attribute is + +.......... +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "voter" : true, + "height" : 6.1, + "address" : { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021-3100" + }, + "phoneNumbers": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "office", + "number": "646 555-4567" + } + ] + } +.......... + +.jsonPathSet Examples +|=================================================================== +| Expression | Value +| `${myJson:jsonPathSet('$.firstName', 'James')}` | `{"firstName":"James", lastName":"Smith", "age":25, "voter":true, "height":6.1, "address":{"streetAddress":"21 2nd Street", "city":"New York", "state":"NY", "postalCode":"10021-3100"}, "phoneNumbers":[{"type":"home", "number":"212 555-1234"}, {"type":"office", "number":"646 555-4567"}]}` +| `${myJson:jsonPathSet('$.missingpath', 'James')}` | Returns original JSON document +|=================================================================== + +An empty subject value or a subject value with an invalid JSON document results in an exception bulletin. + [[numbers]] == Mathematical Operations and Numeric Manipulation