From fa8ca568a9b7af18f39707d4a7c09b8a435e1c92 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Thu, 27 Jan 2022 15:45:22 +0100 Subject: [PATCH 1/9] [US642] Implement filtering --- .../github/fge/jsonpatch/AddOperation.java | 57 +++++++++++++------ .../github/fge/jsonpatch/JsonPathParser.java | 26 ++++++++- .../fge/jsonpatch/JsonPathParserTest.java | 33 +++++++++++ .../query/AddQueryOperationTest.java | 6 +- .../query/RemoveQueryOperationTest.java | 5 +- .../query/ReplaceQueryOperationTest.java | 5 +- .../resources/jsonpatch/query/filter.json | 50 ++++++++++++++++ .../resources/jsonpatch/query/remove.json | 5 +- .../resources/jsonpatch/query/replace.json | 22 ++++--- 9 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java create mode 100644 src/test/resources/jsonpatch/query/filter.json diff --git a/src/main/java/com/github/fge/jsonpatch/AddOperation.java b/src/main/java/com/github/fge/jsonpatch/AddOperation.java index 66d86cf5..50925241 100644 --- a/src/main/java/com/github/fge/jsonpatch/AddOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/AddOperation.java @@ -83,13 +83,14 @@ public JsonNode apply(final JsonNode node) throws JsonPatchException { * Check the parent node: it must exist and be a container (ie an array * or an object) for the add operation to work. */ - final int lastSlashIndex = path.lastIndexOf('/'); - final String newNodeName = path.substring(lastSlashIndex + 1); - final String pathToParent = path.substring(0, lastSlashIndex); - final String jsonPath = JsonPathParser.tmfStringToJsonPath(pathToParent); + final String fullJsonPath = JsonPathParser.tmfStringToJsonPath(path); + final int lastDotIndex = fullJsonPath.lastIndexOf('.'); + final String newNodeName = fullJsonPath.substring(lastDotIndex + 1); + final String pathToParent = fullJsonPath.substring(0, lastDotIndex); + final DocumentContext nodeContext = JsonPath.parse(node.deepCopy()); - final JsonNode parentNode = nodeContext.read(jsonPath); + final JsonNode parentNode = nodeContext.read(pathToParent); if (parentNode == null) { throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchParent")); } @@ -97,9 +98,23 @@ public JsonNode apply(final JsonNode node) throws JsonPatchException { throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer")); } - return parentNode.isArray() - ? addToArray(nodeContext, jsonPath, newNodeName) - : addToObject(nodeContext, jsonPath, newNodeName); + // result will be a list of nodes + if (pathToParent.contains("[?(")) { + for (int i = 0; i < parentNode.size(); i++) { + JsonNode containerNode = parentNode.get(i); + DocumentContext containerContext = JsonPath.parse(containerNode); + if (containerNode.isArray()) { + addToArray(containerContext, "$", newNodeName); + } else { + addToObject(containerContext, "$", newNodeName); + } + } + return nodeContext.read("$"); + } else { + return parentNode.isArray() + ? addToArray(nodeContext, pathToParent, newNodeName) + : addToObject(nodeContext, pathToParent, newNodeName); + } } private JsonNode addToArray(final DocumentContext node, String jsonPath, String newNodeName) throws JsonPatchException { @@ -108,22 +123,30 @@ private JsonNode addToArray(final DocumentContext node, String jsonPath, String } final int size = node.read(jsonPath, JsonNode.class).size(); - final int index; - try { - index = Integer.parseInt(newNodeName); - } catch (NumberFormatException ignored) { - throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.notAnIndex")); - } - if (index < 0 || index > size) - throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchIndex")); + final int index = verifyAndGetArrayIndex(newNodeName, size); ArrayNode updatedArray = node.read(jsonPath, ArrayNode.class).insert(index, value); return "$".equals(jsonPath) ? updatedArray : node.set(jsonPath, updatedArray).read("$", JsonNode.class); } private JsonNode addToObject(final DocumentContext node, String jsonPath, String newNodeName) { + String strippedName = newNodeName.replace("[", "").replace("]", ""); return node - .put(jsonPath, newNodeName, value) + .put(jsonPath, strippedName, value) .read("$", JsonNode.class); } + + private int verifyAndGetArrayIndex(String wrappedIndex, int size) throws JsonPatchException { + final String stringIndex = wrappedIndex.replace("[", "").replace("]", ""); + int index; + try { + index = Integer.parseInt(stringIndex); + } catch (NumberFormatException ignored) { + throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.notAnIndex")); + } + if (index < 0 || index > size) { + throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchIndex")); + } + return index; + } } diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 00467216..2639f16d 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -9,9 +9,31 @@ public static String tmfStringToJsonPath(String path) { if ("/".equals(path)) { return "$"; } - final String jsonPath = "$" + path.replace('/', '.') + final String[] pointerAndQuery = path.split("\\?", -1); + if (pointerAndQuery.length > 2) { + // TODO use different exception + throw new RuntimeException("Invalid query, only one `?` allowed."); + } + + final String jsonPath = "$" + pointerAndQuery[0].replace('/', '.') .replaceAll(ARRAY_ELEMENT_REGEX, ".[$1].") + .replaceAll(ARRAY_ELEMENT_REGEX, ".[$1].") // has to be repeated due to positive lookahead not working properly .replaceAll(ARRAY_ELEMENT_LAST_REGEX, ".[$1]"); - return jsonPath; + final String jsonPathWithQuery = addQueryIfApplicable(jsonPath, pointerAndQuery); + return jsonPathWithQuery; + } + + private static String addQueryIfApplicable(String jsonPath, String[] pointerAndQuery) { + if (pointerAndQuery.length == 2) { + String preparedFilter = pointerAndQuery[1] + .replaceAll("=", "==") + .replaceAll("==([\\w ]+)", "=='$1'") + .replaceFirst("\\w+", "@") + .replaceAll("&\\w+", " && @") + .replaceAll("\\|\\w+", " || @");//TODO are logical ORs supported? + return jsonPath.replaceFirst("(\\w+)", "$1[?(" + preparedFilter + ")]"); + } else { + return jsonPath; + } } } diff --git a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java new file mode 100644 index 00000000..4f84c100 --- /dev/null +++ b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java @@ -0,0 +1,33 @@ +package com.github.fge.jsonpatch; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class JsonPathParserTest { + + @Test + public void shouldConvertQueryToJsonPath() { + String jsonPointerWithQuery = "/productPrice/prodPriceAlteration?productPrice.name=Regular Price"; + String expected = "$.productPrice[?(@.name=='Regular Price')].prodPriceAlteration"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + + @Test + public void shouldConvertArrayPathToJsonPath() { + String jsonPointerWithQuery = "/2/1/-"; + String expected = "$.[2].[1].-"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + + @Test + public void shouldConvertManyConditions() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary"; + String expected = "$.orderItem[?(@.productOffering.id=='1513' && @.product.relatedParty.role=='customer' && @.product.relatedParty.name=='Mary')].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + +} \ No newline at end of file diff --git a/src/test/java/com/github/fge/jsonpatch/query/AddQueryOperationTest.java b/src/test/java/com/github/fge/jsonpatch/query/AddQueryOperationTest.java index 00c087b6..7ca4c0eb 100644 --- a/src/test/java/com/github/fge/jsonpatch/query/AddQueryOperationTest.java +++ b/src/test/java/com/github/fge/jsonpatch/query/AddQueryOperationTest.java @@ -3,10 +3,10 @@ import com.github.fge.jsonpatch.JsonPatchOperationTest; import java.io.IOException; -// TODO extend with JsonPatchOperationTest and uncomment constructor when this test needs to be active, couldn't ignore it otherway -public class AddQueryOperationTest extends Object { + +public class AddQueryOperationTest extends JsonPatchOperationTest { public AddQueryOperationTest() throws IOException { - //super("query/add"); + super("query/add"); } } diff --git a/src/test/java/com/github/fge/jsonpatch/query/RemoveQueryOperationTest.java b/src/test/java/com/github/fge/jsonpatch/query/RemoveQueryOperationTest.java index 078e1ee2..3c48ad80 100644 --- a/src/test/java/com/github/fge/jsonpatch/query/RemoveQueryOperationTest.java +++ b/src/test/java/com/github/fge/jsonpatch/query/RemoveQueryOperationTest.java @@ -4,10 +4,9 @@ import java.io.IOException; -// TODO extend with JsonPatchOperationTest and uncomment constructor when this test needs to be active, couldn't ignore it otherway -public class RemoveQueryOperationTest extends Object { +public class RemoveQueryOperationTest extends JsonPatchOperationTest { public RemoveQueryOperationTest() throws IOException { - //super("query/remove"); + super("query/remove"); } } diff --git a/src/test/java/com/github/fge/jsonpatch/query/ReplaceQueryOperationTest.java b/src/test/java/com/github/fge/jsonpatch/query/ReplaceQueryOperationTest.java index f960322e..8b8848bb 100644 --- a/src/test/java/com/github/fge/jsonpatch/query/ReplaceQueryOperationTest.java +++ b/src/test/java/com/github/fge/jsonpatch/query/ReplaceQueryOperationTest.java @@ -4,10 +4,9 @@ import java.io.IOException; -// TODO extend with JsonPatchOperationTest and uncomment constructor when this test needs to be active, couldn't ignore it otherway -public class ReplaceQueryOperationTest extends Object { +public class ReplaceQueryOperationTest extends JsonPatchOperationTest { public ReplaceQueryOperationTest() throws IOException { - //super("query/replace"); + super("query/replace"); } } diff --git a/src/test/resources/jsonpatch/query/filter.json b/src/test/resources/jsonpatch/query/filter.json new file mode 100644 index 00000000..10eadcd5 --- /dev/null +++ b/src/test/resources/jsonpatch/query/filter.json @@ -0,0 +1,50 @@ +{ + "errors": [], + "ops": [ + { + "op": { + "op": "add", + "path": "/note[?(@.author=='John Doe')]", + "value": "{\"text\": \"Informed\"}" + }, + "node" : { + "id": "1", + "note": [{ + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived" + }] + }, + "expected": { + "id": "1", + "note": [{ + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited", + "text": "Informed" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived", + "text": "Informed" + }] + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/jsonpatch/query/remove.json b/src/test/resources/jsonpatch/query/remove.json index 61ff64c6..72a4f5e5 100644 --- a/src/test/resources/jsonpatch/query/remove.json +++ b/src/test/resources/jsonpatch/query/remove.json @@ -1,4 +1,5 @@ { + "errors": [], "ops": [ { "description": "TMF630 - Example 2 - Removing one of the components of an Array Element (the whole structure)", @@ -48,7 +49,7 @@ "description": "TMF630 - Example 3 - Removing an attribute from one of the components of an array", "op": { "op": "remove", - "path": "/productPrice/prodPriceAlteration?prodPrice.name=Regular Price" + "path": "/productPrice/prodPriceAlteration?productPrice.name=Regular Price" }, "node": { "id": "4501", @@ -89,7 +90,7 @@ }, { "description": "TMF630 - Example 4 - Removing a complete complex structure component of an array", - "op": { "op": "remove", "path": "/productPrice? productPrice.name=Setup Price" }, + "op": { "op": "remove", "path": "/productPrice?productPrice.name=Setup Price" }, "node": { "id": "4501", "description": "This product .... ", diff --git a/src/test/resources/jsonpatch/query/replace.json b/src/test/resources/jsonpatch/query/replace.json index 25cd8e9c..1d575538 100644 --- a/src/test/resources/jsonpatch/query/replace.json +++ b/src/test/resources/jsonpatch/query/replace.json @@ -1,11 +1,12 @@ { + "errors": [], "ops": [ { "description": "TMF630 - Example 5 - Replacing an attribute from one of the components of an array", "op": { "op": "replace", "path": "/productOfferingPrice/price/amount?productOfferingPrice.name=Monthly Price", - "value": "25" + "value": 25 }, "node": { "id": "42", @@ -60,7 +61,7 @@ "op": "replace", "path": "/productOfferingPrice/price?productOfferingPrice.name=Setup Price", "value": { - "amount": "40", + "amount": 40, "units": "USD" } }, @@ -81,8 +82,8 @@ "name": "Setup Price", "priceType": "one time", "price": { - "amount": "40", - "units": "USD" + "amount": 30, + "units": "EUR" } } ] @@ -96,7 +97,7 @@ "name": "Monthly Price", "priceType": "recurring", "price": { - "amount": 25, + "amount": 12, "units": "EUR" } }, @@ -104,19 +105,22 @@ "name": "Setup Price", "priceType": "one time", "price": { - "amount": 30, - "units": "EUR" + "amount": 40, + "units": "USD" } } ] } - }, + } + + ], + "notWorking": [ { "description": "TMF630 - Example 7 - Replacing an attribute from one of the components of a complex array (resolving ambiguities)", "op": { "op": "replace", "path": "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary", - "value": "25" + "value": 25 }, "node": { "id": "3774", From 23208050ca0e0eb01bd1fc615e085ef52a95efb4 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Fri, 28 Jan 2022 10:21:05 +0100 Subject: [PATCH 2/9] [US642] Add filtering tests and query parsing --- .../github/fge/jsonpatch/AddOperation.java | 9 +- .../github/fge/jsonpatch/JsonPathParser.java | 3 + .../fge/jsonpatch/JsonPathParserTest.java | 8 ++ .../jsonpatch/query/FilterOperationTest.java | 12 ++ .../resources/jsonpatch/query/filter.json | 127 +++++++++++++++++- 5 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/github/fge/jsonpatch/query/FilterOperationTest.java diff --git a/src/main/java/com/github/fge/jsonpatch/AddOperation.java b/src/main/java/com/github/fge/jsonpatch/AddOperation.java index 50925241..b5e9a30e 100644 --- a/src/main/java/com/github/fge/jsonpatch/AddOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/AddOperation.java @@ -85,7 +85,8 @@ public JsonNode apply(final JsonNode node) throws JsonPatchException { */ final String fullJsonPath = JsonPathParser.tmfStringToJsonPath(path); final int lastDotIndex = fullJsonPath.lastIndexOf('.'); - final String newNodeName = fullJsonPath.substring(lastDotIndex + 1); + final String newNodeName = fullJsonPath.substring(lastDotIndex + 1) + .replace("[", "").replace("]", ""); final String pathToParent = fullJsonPath.substring(0, lastDotIndex); final DocumentContext nodeContext = JsonPath.parse(node.deepCopy()); @@ -130,14 +131,12 @@ private JsonNode addToArray(final DocumentContext node, String jsonPath, String } private JsonNode addToObject(final DocumentContext node, String jsonPath, String newNodeName) { - String strippedName = newNodeName.replace("[", "").replace("]", ""); return node - .put(jsonPath, strippedName, value) + .put(jsonPath, newNodeName, value) .read("$", JsonNode.class); } - private int verifyAndGetArrayIndex(String wrappedIndex, int size) throws JsonPatchException { - final String stringIndex = wrappedIndex.replace("[", "").replace("]", ""); + private int verifyAndGetArrayIndex(String stringIndex, int size) throws JsonPatchException { int index; try { index = Integer.parseInt(stringIndex); diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 2639f16d..7d207278 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -6,6 +6,9 @@ public class JsonPathParser { private static final String ARRAY_ELEMENT_LAST_REGEX = "\\.(\\d+)$"; public static String tmfStringToJsonPath(String path) { + if (!path.startsWith("/") && !path.isEmpty()) { + return "$." + path; + } if ("/".equals(path)) { return "$"; } diff --git a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java index 4f84c100..b6181db1 100644 --- a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java +++ b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java @@ -30,4 +30,12 @@ public void shouldConvertManyConditions() { assertEquals(result, expected); } + @Test + public void shouldConvertFilterQuery() { + String filterQuery = "note[?(@.author=='John Doe')].date"; + String expected = "$.note[?(@.author=='John Doe')].date"; + String result = JsonPathParser.tmfStringToJsonPath(filterQuery); + assertEquals(result, expected); + } + } \ No newline at end of file diff --git a/src/test/java/com/github/fge/jsonpatch/query/FilterOperationTest.java b/src/test/java/com/github/fge/jsonpatch/query/FilterOperationTest.java new file mode 100644 index 00000000..42af19cb --- /dev/null +++ b/src/test/java/com/github/fge/jsonpatch/query/FilterOperationTest.java @@ -0,0 +1,12 @@ +package com.github.fge.jsonpatch.query; + +import com.github.fge.jsonpatch.JsonPatchOperationTest; + +import java.io.IOException; + +public class FilterOperationTest extends JsonPatchOperationTest { + + public FilterOperationTest() throws IOException { + super("query/filter"); + } +} diff --git a/src/test/resources/jsonpatch/query/filter.json b/src/test/resources/jsonpatch/query/filter.json index 10eadcd5..2f230505 100644 --- a/src/test/resources/jsonpatch/query/filter.json +++ b/src/test/resources/jsonpatch/query/filter.json @@ -4,8 +4,8 @@ { "op": { "op": "add", - "path": "/note[?(@.author=='John Doe')]", - "value": "{\"text\": \"Informed\"}" + "path": "note[?(@.author=='John Doe')].text", + "value": "Informed" }, "node" : { "id": "1", @@ -45,6 +45,129 @@ "text": "Informed" }] } + }, + { + "op": { + "op": "add", + "path": "note[?(@.author=='John Doe' && @.status=='Edited')].text", + "value": "Informed" + }, + "node": { + "id": "1", + "note": [ + { + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived" + } + ] + }, + "expected": { + "id": "1", + "note": [ + { + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited", + "text": "Informed" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived" + } + ] + } + }, + { + "op": { + "op": "remove", + "path": "note[?(@.author=='John Doe')].date", + "value": "{\"text\": \"Informed\"}" + }, + "node" : { + "id": "1", + "note": [{ + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived" + }] + }, + "expected": { + "id": "1", + "note": [{ + "author": "John Doe", + "status": "Edited" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "author": "John Doe", + "status": "Archived" + }] + } + }, + { + "op": { + "op": "remove", + "path": "note[?(@.author=='John Doe')]" + }, + "node" : { + "id": "1", + "note": [{ + "date": "2013-07-25T06:55:12.0Z", + "author": "John Doe", + "status": "Edited" + }, + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "status": "Archived" + }] + }, + "expected": { + "id": "1", + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "status": "Edited" + } + ] + } } ] } \ No newline at end of file From 691b7be05fc5cfd4531a2492d14745c3d1019dbd Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Fri, 28 Jan 2022 13:31:55 +0100 Subject: [PATCH 3/9] [US642] Handle booleans, integers and floating point numbers --- .../github/fge/jsonpatch/JsonPathParser.java | 8 +- .../fge/jsonpatch/JsonPathParserTest.java | 28 +- .../jsonpatch/query/IndividualEntityTest.java | 12 + src/test/resources/jsonpatch/query/add.json | 68 ++ .../resources/jsonpatch/query/individual.json | 645 ++++++++++++++++++ 5 files changed, 757 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/github/fge/jsonpatch/query/IndividualEntityTest.java create mode 100644 src/test/resources/jsonpatch/query/individual.json diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 7d207278..66d9c9d3 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -30,11 +30,15 @@ private static String addQueryIfApplicable(String jsonPath, String[] pointerAndQ if (pointerAndQuery.length == 2) { String preparedFilter = pointerAndQuery[1] .replaceAll("=", "==") - .replaceAll("==([\\w ]+)", "=='$1'") + .replaceAll("==([\\w .]+)", "=='$1'") .replaceFirst("\\w+", "@") .replaceAll("&\\w+", " && @") .replaceAll("\\|\\w+", " || @");//TODO are logical ORs supported? - return jsonPath.replaceFirst("(\\w+)", "$1[?(" + preparedFilter + ")]"); + String filterWithBooleansAndNumbers = preparedFilter + .replaceAll("@([\\w.]+)=='(true|false)'", "(@$1==$2 || @$1=='$2')") + .replaceAll("@([\\w.]+)=='(\\d+)'", "(@$1==$2 || @$1=='$2')") + .replaceAll("@([\\w.]+)=='(\\d+\\.\\d+)'", "(@$1==$2 || @$1=='$2')"); + return jsonPath.replaceFirst("(\\w+)", "$1[?(" + filterWithBooleansAndNumbers + ")]"); } else { return jsonPath; } diff --git a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java index b6181db1..93c10baf 100644 --- a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java +++ b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java @@ -22,10 +22,34 @@ public void shouldConvertArrayPathToJsonPath() { assertEquals(result, expected); } + @Test + public void shouldConvertBooleans() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.valid=true&orderItem.product.relatedParty.role=customer"; + String expected = "$.orderItem[?((@.productOffering.valid==true || @.productOffering.valid=='true') && @.product.relatedParty.role=='customer')].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + + @Test + public void shouldConvertFloatingPoint() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.price=1513.77&orderItem.product.relatedParty.role=customer"; + String expected = "$.orderItem[?((@.productOffering.price==1513.77 || @.productOffering.price=='1513.77') && @.product.relatedParty.role=='customer')].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + + @Test + public void shouldConvertIntegers() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer"; + String expected = "$.orderItem[?((@.productOffering.id==1513 || @.productOffering.id=='1513') && @.product.relatedParty.role=='customer')].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + @Test public void shouldConvertManyConditions() { - String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary"; - String expected = "$.orderItem[?(@.productOffering.id=='1513' && @.product.relatedParty.role=='customer' && @.product.relatedParty.name=='Mary')].quantity"; + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary"; + String expected = "$.orderItem[?(@.product.relatedParty.role=='customer' && @.product.relatedParty.name=='Mary')].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); assertEquals(result, expected); } diff --git a/src/test/java/com/github/fge/jsonpatch/query/IndividualEntityTest.java b/src/test/java/com/github/fge/jsonpatch/query/IndividualEntityTest.java new file mode 100644 index 00000000..7ee114bb --- /dev/null +++ b/src/test/java/com/github/fge/jsonpatch/query/IndividualEntityTest.java @@ -0,0 +1,12 @@ +package com.github.fge.jsonpatch.query; + +import com.github.fge.jsonpatch.JsonPatchOperationTest; + +import java.io.IOException; + +public class IndividualEntityTest extends JsonPatchOperationTest { + + public IndividualEntityTest() throws IOException { + super("query/individual"); + } +} diff --git a/src/test/resources/jsonpatch/query/add.json b/src/test/resources/jsonpatch/query/add.json index 43e70b64..b1b98d09 100644 --- a/src/test/resources/jsonpatch/query/add.json +++ b/src/test/resources/jsonpatch/query/add.json @@ -1,6 +1,74 @@ { "errors": [], "ops": [ + { + "op": { + "op": "add", + "path": "/note/text?note.verified=true", + "value":"Informed" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [{ + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + }] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [{ + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "text": "Informed" + }] + } + }, + { + "op": { + "op": "add", + "path": "/note/text?note.author=John Doe", + "value":"Informed" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [{ + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + }] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [{ + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "text": "Informed" + }] + } + }, { "description": "TMF630 - Example 1 - Adding an attribute to one of the components of an array", "op": { diff --git a/src/test/resources/jsonpatch/query/individual.json b/src/test/resources/jsonpatch/query/individual.json new file mode 100644 index 00000000..da7eb0a5 --- /dev/null +++ b/src/test/resources/jsonpatch/query/individual.json @@ -0,0 +1,645 @@ +{ + "errors": [], + "ops": [ + { + "op": { + "op": "add", + "path": "/contactMedium/wrapper?contactMedium.mediumType=qRXcJlpNjx", + "value": "12" + }, + "node": { + "aristocraticTitle": "Baron", + "contactMedium": [ + { + "mediumType": "qRXcJlpNjx", + "preferred": false, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + }, + { + "mediumType": "asfasdfgasdf", + "preferred": true, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + } + ], + "creditRating": [ + { + "creditAgencyName": "wrSavzqmHm", + "ratingScore": 66, + "validFor": { + "endDateTime": "2021-07-31T11:00:17Z", + "startDateTime": "2021-07-30T10:21:17Z" + } + } + ], + "disability": [ + { + "disabilityCode": "QgHhbBLahC", + "disabilityName": "ouqGwsvLEf", + "validFor": { + "endDateTime": "2021-08-03T04:29:17Z", + "startDateTime": "2021-07-28T00:27:17Z" + } + } + ], + "externalReference": [ + { + "externalReferenceType": "aIOPiNhbmo", + "name": "RIlMwNlVmu" + } + ], + "individualIdentification": [ + { + "identificationId": "DqReHbjoAZ", + "issuingDate": "2021-07-31T09:46:17Z", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "href": "OXMkUnCJZU", + "size": { + "amount": 744, + "units": "JCxEtmpdJp" + }, + "validFor": { + "endDateTime": "2021-08-01T23:24:17Z", + "startDateTime": "2021-07-28T01:50:17Z" + } + }, + "validFor": { + "endDateTime": "2021-08-03T23:35:17Z", + "startDateTime": "2021-07-30T06:43:17Z" + } + } + ], + "languageAbility": [ + { + "isFavouriteLanguage": false, + "languageCode": "ZcpsxgQNhM", + "writingProficiency": "zWYDbRYonQ", + "validFor": { + "endDateTime": "2021-08-02T05:17:17Z", + "startDateTime": "2021-07-28T22:04:17Z" + } + } + ], + "otherName": [ + { + "aristocraticTitle": "qpMBXqEnNP", + "title": "mlfshyAJHW", + "validFor": { + "endDateTime": "2021-08-01T06:06:17Z", + "startDateTime": "2021-07-29T14:22:17Z" + } + } + ], + "partyCharacteristic": [ + { + "name": "OKMcApMuEM", + "valueType": "sdraPevzAe", + "value": "HEIlNQxeHi" + } + ], + "relatedParty": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "href": "WRYrNaAtsf", + "name": "EYOufmlArK", + "role": "OtbxPWgead", + "@referredType": "idUvdppQWd" + } + ], + "skill": [ + { + "comment": "yfFsTAPwkJ", + "evaluatedLevel": "kahUzknbkv", + "skillCode": "UjeonFMegY", + "skillName": "NdjXHHuZze", + "validFor": { + "endDateTime": "2021-07-30T22:35:17Z", + "startDateTime": "2021-07-28T09:45:17Z" + } + } + ], + "status": "initialized", + "taxExemptionCertificate": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "size": { + "amount": 399, + "units": "nEbqvKVtps" + }, + "validFor": { + "endDateTime": "2021-08-01T15:27:17Z", + "startDateTime": "2021-07-27T16:20:17Z" + } + }, + "taxDefinition": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "name": "shnwvwDoob", + "taxType": "wjtBLFqKRt" + } + ], + "validFor": { + "endDateTime": "2021-08-01T12:01:17Z", + "startDateTime": "2021-07-30T05:23:17Z" + } + } + ] + }, + "expected": { + "aristocraticTitle": "Baron", + "contactMedium": [ + { + "mediumType": "qRXcJlpNjx", + "preferred": false, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + }, + "wrapper": "12" + }, + { + "mediumType": "asfasdfgasdf", + "preferred": true, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + } + ], + "creditRating": [ + { + "creditAgencyName": "wrSavzqmHm", + "ratingScore": 66, + "validFor": { + "endDateTime": "2021-07-31T11:00:17Z", + "startDateTime": "2021-07-30T10:21:17Z" + } + } + ], + "disability": [ + { + "disabilityCode": "QgHhbBLahC", + "disabilityName": "ouqGwsvLEf", + "validFor": { + "endDateTime": "2021-08-03T04:29:17Z", + "startDateTime": "2021-07-28T00:27:17Z" + } + } + ], + "externalReference": [ + { + "externalReferenceType": "aIOPiNhbmo", + "name": "RIlMwNlVmu" + } + ], + "individualIdentification": [ + { + "identificationId": "DqReHbjoAZ", + "issuingDate": "2021-07-31T09:46:17Z", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "href": "OXMkUnCJZU", + "size": { + "amount": 744, + "units": "JCxEtmpdJp" + }, + "validFor": { + "endDateTime": "2021-08-01T23:24:17Z", + "startDateTime": "2021-07-28T01:50:17Z" + } + }, + "validFor": { + "endDateTime": "2021-08-03T23:35:17Z", + "startDateTime": "2021-07-30T06:43:17Z" + } + } + ], + "languageAbility": [ + { + "isFavouriteLanguage": false, + "languageCode": "ZcpsxgQNhM", + "writingProficiency": "zWYDbRYonQ", + "validFor": { + "endDateTime": "2021-08-02T05:17:17Z", + "startDateTime": "2021-07-28T22:04:17Z" + } + } + ], + "otherName": [ + { + "aristocraticTitle": "qpMBXqEnNP", + "title": "mlfshyAJHW", + "validFor": { + "endDateTime": "2021-08-01T06:06:17Z", + "startDateTime": "2021-07-29T14:22:17Z" + } + } + ], + "partyCharacteristic": [ + { + "name": "OKMcApMuEM", + "valueType": "sdraPevzAe", + "value": "HEIlNQxeHi" + } + ], + "relatedParty": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "href": "WRYrNaAtsf", + "name": "EYOufmlArK", + "role": "OtbxPWgead", + "@referredType": "idUvdppQWd" + } + ], + "skill": [ + { + "comment": "yfFsTAPwkJ", + "evaluatedLevel": "kahUzknbkv", + "skillCode": "UjeonFMegY", + "skillName": "NdjXHHuZze", + "validFor": { + "endDateTime": "2021-07-30T22:35:17Z", + "startDateTime": "2021-07-28T09:45:17Z" + } + } + ], + "status": "initialized", + "taxExemptionCertificate": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "size": { + "amount": 399, + "units": "nEbqvKVtps" + }, + "validFor": { + "endDateTime": "2021-08-01T15:27:17Z", + "startDateTime": "2021-07-27T16:20:17Z" + } + }, + "taxDefinition": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "name": "shnwvwDoob", + "taxType": "wjtBLFqKRt" + } + ], + "validFor": { + "endDateTime": "2021-08-01T12:01:17Z", + "startDateTime": "2021-07-30T05:23:17Z" + } + } + ] + } + }, + { + "op": { + "op": "add", + "path": "/contactMedium/wrapper?contactMedium.preferred=false", + "value": {"inside": 12} + }, + "node": { + "aristocraticTitle": "Baron", + "contactMedium": [ + { + "mediumType": "qRXcJlpNjx", + "preferred": false, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + }, + { + "mediumType": "asfasdfgasdf", + "preferred": true, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + } + ], + "creditRating": [ + { + "creditAgencyName": "wrSavzqmHm", + "ratingScore": 66, + "validFor": { + "endDateTime": "2021-07-31T11:00:17Z", + "startDateTime": "2021-07-30T10:21:17Z" + } + } + ], + "disability": [ + { + "disabilityCode": "QgHhbBLahC", + "disabilityName": "ouqGwsvLEf", + "validFor": { + "endDateTime": "2021-08-03T04:29:17Z", + "startDateTime": "2021-07-28T00:27:17Z" + } + } + ], + "externalReference": [ + { + "externalReferenceType": "aIOPiNhbmo", + "name": "RIlMwNlVmu" + } + ], + "individualIdentification": [ + { + "identificationId": "DqReHbjoAZ", + "issuingDate": "2021-07-31T09:46:17Z", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "href": "OXMkUnCJZU", + "size": { + "amount": 744, + "units": "JCxEtmpdJp" + }, + "validFor": { + "endDateTime": "2021-08-01T23:24:17Z", + "startDateTime": "2021-07-28T01:50:17Z" + } + }, + "validFor": { + "endDateTime": "2021-08-03T23:35:17Z", + "startDateTime": "2021-07-30T06:43:17Z" + } + } + ], + "languageAbility": [ + { + "isFavouriteLanguage": false, + "languageCode": "ZcpsxgQNhM", + "writingProficiency": "zWYDbRYonQ", + "validFor": { + "endDateTime": "2021-08-02T05:17:17Z", + "startDateTime": "2021-07-28T22:04:17Z" + } + } + ], + "otherName": [ + { + "aristocraticTitle": "qpMBXqEnNP", + "title": "mlfshyAJHW", + "validFor": { + "endDateTime": "2021-08-01T06:06:17Z", + "startDateTime": "2021-07-29T14:22:17Z" + } + } + ], + "partyCharacteristic": [ + { + "name": "OKMcApMuEM", + "valueType": "sdraPevzAe", + "value": "HEIlNQxeHi" + } + ], + "relatedParty": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "href": "WRYrNaAtsf", + "name": "EYOufmlArK", + "role": "OtbxPWgead", + "@referredType": "idUvdppQWd" + } + ], + "skill": [ + { + "comment": "yfFsTAPwkJ", + "evaluatedLevel": "kahUzknbkv", + "skillCode": "UjeonFMegY", + "skillName": "NdjXHHuZze", + "validFor": { + "endDateTime": "2021-07-30T22:35:17Z", + "startDateTime": "2021-07-28T09:45:17Z" + } + } + ], + "status": "initialized", + "taxExemptionCertificate": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "size": { + "amount": 399, + "units": "nEbqvKVtps" + }, + "validFor": { + "endDateTime": "2021-08-01T15:27:17Z", + "startDateTime": "2021-07-27T16:20:17Z" + } + }, + "taxDefinition": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "name": "shnwvwDoob", + "taxType": "wjtBLFqKRt" + } + ], + "validFor": { + "endDateTime": "2021-08-01T12:01:17Z", + "startDateTime": "2021-07-30T05:23:17Z" + } + } + ] + }, + "expected": { + "aristocraticTitle": "Baron", + "contactMedium": [ + { + "mediumType": "qRXcJlpNjx", + "preferred": false, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + }, + "wrapper": {"inside": 12} + }, + { + "mediumType": "asfasdfgasdf", + "preferred": true, + "characteristic": { + "addressKeyType": "NAD", + "addressKey": "wunMHlcIQP", + "city": "kiNnXHrWwz" + }, + "validFor": { + "endDateTime": "2021-08-02T12:31:17Z", + "startDateTime": "2021-07-28T16:33:17Z" + } + } + ], + "creditRating": [ + { + "creditAgencyName": "wrSavzqmHm", + "ratingScore": 66, + "validFor": { + "endDateTime": "2021-07-31T11:00:17Z", + "startDateTime": "2021-07-30T10:21:17Z" + } + } + ], + "disability": [ + { + "disabilityCode": "QgHhbBLahC", + "disabilityName": "ouqGwsvLEf", + "validFor": { + "endDateTime": "2021-08-03T04:29:17Z", + "startDateTime": "2021-07-28T00:27:17Z" + } + } + ], + "externalReference": [ + { + "externalReferenceType": "aIOPiNhbmo", + "name": "RIlMwNlVmu" + } + ], + "individualIdentification": [ + { + "identificationId": "DqReHbjoAZ", + "issuingDate": "2021-07-31T09:46:17Z", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "href": "OXMkUnCJZU", + "size": { + "amount": 744, + "units": "JCxEtmpdJp" + }, + "validFor": { + "endDateTime": "2021-08-01T23:24:17Z", + "startDateTime": "2021-07-28T01:50:17Z" + } + }, + "validFor": { + "endDateTime": "2021-08-03T23:35:17Z", + "startDateTime": "2021-07-30T06:43:17Z" + } + } + ], + "languageAbility": [ + { + "isFavouriteLanguage": false, + "languageCode": "ZcpsxgQNhM", + "writingProficiency": "zWYDbRYonQ", + "validFor": { + "endDateTime": "2021-08-02T05:17:17Z", + "startDateTime": "2021-07-28T22:04:17Z" + } + } + ], + "otherName": [ + { + "aristocraticTitle": "qpMBXqEnNP", + "title": "mlfshyAJHW", + "validFor": { + "endDateTime": "2021-08-01T06:06:17Z", + "startDateTime": "2021-07-29T14:22:17Z" + } + } + ], + "partyCharacteristic": [ + { + "name": "OKMcApMuEM", + "valueType": "sdraPevzAe", + "value": "HEIlNQxeHi" + } + ], + "relatedParty": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "href": "WRYrNaAtsf", + "name": "EYOufmlArK", + "role": "OtbxPWgead", + "@referredType": "idUvdppQWd" + } + ], + "skill": [ + { + "comment": "yfFsTAPwkJ", + "evaluatedLevel": "kahUzknbkv", + "skillCode": "UjeonFMegY", + "skillName": "NdjXHHuZze", + "validFor": { + "endDateTime": "2021-07-30T22:35:17Z", + "startDateTime": "2021-07-28T09:45:17Z" + } + } + ], + "status": "initialized", + "taxExemptionCertificate": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "attachment": { + "id": "61bb19204b8a355d3bdb45a4", + "size": { + "amount": 399, + "units": "nEbqvKVtps" + }, + "validFor": { + "endDateTime": "2021-08-01T15:27:17Z", + "startDateTime": "2021-07-27T16:20:17Z" + } + }, + "taxDefinition": [ + { + "id": "61bb19204b8a355d3bdb45a4", + "name": "shnwvwDoob", + "taxType": "wjtBLFqKRt" + } + ], + "validFor": { + "endDateTime": "2021-08-01T12:01:17Z", + "startDateTime": "2021-07-30T05:23:17Z" + } + } + ] + } + } + ] +} \ No newline at end of file From d8ca90b240e118e72d20d30b37d3708e488167d7 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Fri, 28 Jan 2022 15:29:58 +0100 Subject: [PATCH 4/9] [US642] Add some test cases --- src/test/resources/jsonpatch/query/add.json | 130 ++++++++++++++------ 1 file changed, 93 insertions(+), 37 deletions(-) diff --git a/src/test/resources/jsonpatch/query/add.json b/src/test/resources/jsonpatch/query/add.json index b1b98d09..63a4e6d8 100644 --- a/src/test/resources/jsonpatch/query/add.json +++ b/src/test/resources/jsonpatch/query/add.json @@ -5,68 +5,120 @@ "op": { "op": "add", "path": "/note/text?note.verified=true", - "value":"Informed" + "value": "Informed" }, "node": { "id": "1", "correlationId": "TT53482", - "note": [{ - "verified": false, - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, { "verified": true, "date": "2013-07-25T08:55:12.0Z", "author": "John Doe" - }] + } + ] }, "expected": { "id": "1", "correlationId": "TT53482", - "note": [{ - "verified": false, - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, { "verified": true, "date": "2013-07-25T08:55:12.0Z", "author": "John Doe", "text": "Informed" - }] + } + ] } }, { "op": { "op": "add", - "path": "/note/text?note.author=John Doe", - "value":"Informed" + "path": "/note/text?note.price=8.5", + "value": "Informed" }, "node": { "id": "1", "correlationId": "TT53482", - "note": [{ - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "price": 16 + }, { "date": "2013-07-25T08:55:12.0Z", - "author": "John Doe" - }] + "author": "John Doe", + "price": 8.5 + } + ] }, "expected": { "id": "1", "correlationId": "TT53482", - "note": [{ - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "price": 16 + }, { "date": "2013-07-25T08:55:12.0Z", "author": "John Doe", + "price": 8.5, "text": "Informed" - }] + } + ] + } + }, + { + "op": { + "op": "add", + "path": "/note/text?note.price>9", + "value": "Informed" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "price": 16 + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "price": 8.5 + } + ] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "price": 16, + "text": "Informed" + }, + { + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "price": 8.5 + } + ] } }, { @@ -74,32 +126,36 @@ "op": { "op": "add", "path": "/note/text?note.author=John Doe", - "value":"Informed" + "value": "Informed" }, "node": { "id": "1", "correlationId": "TT53482", - "note": [{ - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, { "date": "2013-07-25T08:55:12.0Z", "author": "John Doe" - }] + } + ] }, "expected": { "id": "1", "correlationId": "TT53482", - "note": [{ - "date": "2013-07-24T09:55:30.0Z", - "author": "Arthur Evans" - }, + "note": [ + { + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, { "date": "2013-07-25T08:55:12.0Z", "author": "John Doe", "text": "Informed" - }] + } + ] } } ] From 848af1653764ede31041263015a6bcb5d985cc33 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Wed, 2 Feb 2022 11:55:52 +0100 Subject: [PATCH 5/9] [US642] Handle nested array filter queries --- .../github/fge/jsonpatch/JsonPathParser.java | 20 ++++++++++--------- .../fge/jsonpatch/JsonPathParserTest.java | 16 +++++++++++++++ .../resources/jsonpatch/query/replace.json | 7 ++----- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 66d9c9d3..2a4750cd 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -12,7 +12,9 @@ public static String tmfStringToJsonPath(String path) { if ("/".equals(path)) { return "$"; } - final String[] pointerAndQuery = path.split("\\?", -1); + final String[] pointerAndQuery = path + .replaceAll("(\\w)\\?", "$1#") + .split("#", -1); if (pointerAndQuery.length > 2) { // TODO use different exception throw new RuntimeException("Invalid query, only one `?` allowed."); @@ -29,15 +31,15 @@ public static String tmfStringToJsonPath(String path) { private static String addQueryIfApplicable(String jsonPath, String[] pointerAndQuery) { if (pointerAndQuery.length == 2) { String preparedFilter = pointerAndQuery[1] - .replaceAll("=", "==") - .replaceAll("==([\\w .]+)", "=='$1'") - .replaceFirst("\\w+", "@") - .replaceAll("&\\w+", " && @") - .replaceAll("\\|\\w+", " || @");//TODO are logical ORs supported? + .replaceAll("]", "] empty false") // add empty false to nested array expressions + .replaceAll("(\\w)=(\\w)", "$1==$2") // replace single equals with double + .replaceAll("==([\\w .]+)", "=='$1'") // surround strings with single quotes + .replaceFirst("\\w+", "@") // jsonpath expression should start with @ as the name of item + .replaceAll("([&|])\\w+", " $1$1 @"); // replace single | and & with doubles String filterWithBooleansAndNumbers = preparedFilter - .replaceAll("@([\\w.]+)=='(true|false)'", "(@$1==$2 || @$1=='$2')") - .replaceAll("@([\\w.]+)=='(\\d+)'", "(@$1==$2 || @$1=='$2')") - .replaceAll("@([\\w.]+)=='(\\d+\\.\\d+)'", "(@$1==$2 || @$1=='$2')"); + .replaceAll("@([\\w.]+)=='(true|false)'", "(@$1==$2 || @$1=='$2')") // prepare a statement for boolean and boolean as string + .replaceAll("@([\\w.]+)=='(\\d+)'", "(@$1==$2 || @$1=='$2')") // prepare a statement for an integer and integer as string + .replaceAll("@([\\w.]+)=='(\\d+\\.\\d+)'", "(@$1==$2 || @$1=='$2')"); // prepare a statement for float and float as string return jsonPath.replaceFirst("(\\w+)", "$1[?(" + filterWithBooleansAndNumbers + ")]"); } else { return jsonPath; diff --git a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java index 93c10baf..1e2d2c14 100644 --- a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java +++ b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java @@ -54,6 +54,22 @@ public void shouldConvertManyConditions() { assertEquals(result, expected); } + @Test + public void shouldConvertNestedArrayQuery() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty[?(@.role=='customer' && @.name=='Mary')]"; + String expected = "$.orderItem[?((@.productOffering.id==1513 || @.productOffering.id=='1513') && @.product.relatedParty[?(@.role=='customer' && @.name=='Mary')] empty false)].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + + @Test + public void shouldConvertNestedArrayQueryWhichIsNotLastStatement() { + String jsonPointerWithQuery = "/orderItem/quantity?orderItem.product.relatedParty[?(@.role=='customer' && @.name=='Mary')]&orderItem.productOffering.id=1513"; + String expected = "$.orderItem[?(@.product.relatedParty[?(@.role=='customer' && @.name=='Mary')] empty false && (@.productOffering.id==1513 || @.productOffering.id=='1513'))].quantity"; + String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); + assertEquals(result, expected); + } + @Test public void shouldConvertFilterQuery() { String filterQuery = "note[?(@.author=='John Doe')].date"; diff --git a/src/test/resources/jsonpatch/query/replace.json b/src/test/resources/jsonpatch/query/replace.json index 1d575538..339cd6f2 100644 --- a/src/test/resources/jsonpatch/query/replace.json +++ b/src/test/resources/jsonpatch/query/replace.json @@ -111,15 +111,12 @@ } ] } - } - - ], - "notWorking": [ + }, { "description": "TMF630 - Example 7 - Replacing an attribute from one of the components of a complex array (resolving ambiguities)", "op": { "op": "replace", - "path": "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary", + "path": "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty[?(@.role=='customer' && @.name=='Mary')]", "value": 25 }, "node": { From afa9dcd7385182c2d2acb9847fa3a640674044d2 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Wed, 2 Feb 2022 14:41:33 +0100 Subject: [PATCH 6/9] [US642] Add a couple more tests for an add operation --- .../github/fge/jsonpatch/JsonPathParser.java | 7 +- .../github/fge/jsonpatch/messages.properties | 1 + .../fge/jsonpatch/JsonPathParserTest.java | 18 +- src/test/resources/jsonpatch/query/add.json | 164 ++++++++++++++++++ 4 files changed, 178 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 2a4750cd..57fcc20e 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -1,11 +1,13 @@ package com.github.fge.jsonpatch; +import static com.github.fge.jsonpatch.JsonPatchOperation.BUNDLE; + public class JsonPathParser { private static final String ARRAY_ELEMENT_REGEX = "\\.(\\d+)\\."; private static final String ARRAY_ELEMENT_LAST_REGEX = "\\.(\\d+)$"; - public static String tmfStringToJsonPath(String path) { + public static String tmfStringToJsonPath(String path) throws JsonPatchException { if (!path.startsWith("/") && !path.isEmpty()) { return "$." + path; } @@ -16,8 +18,7 @@ public static String tmfStringToJsonPath(String path) { .replaceAll("(\\w)\\?", "$1#") .split("#", -1); if (pointerAndQuery.length > 2) { - // TODO use different exception - throw new RuntimeException("Invalid query, only one `?` allowed."); + throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.invalidPathExpression")); } final String jsonPath = "$" + pointerAndQuery[0].replace('/', '.') diff --git a/src/main/resources/com/github/fge/jsonpatch/messages.properties b/src/main/resources/com/github/fge/jsonpatch/messages.properties index 668240ca..7bee4508 100644 --- a/src/main/resources/com/github/fge/jsonpatch/messages.properties +++ b/src/main/resources/com/github/fge/jsonpatch/messages.properties @@ -27,4 +27,5 @@ jsonPatch.noSuchIndex=no such index in target array jsonPatch.noSuchPath=no such path in target JSON document jsonPatch.parentNotContainer=parent of path to add to is not a container jsonPatch.valueTestFailure=value differs from expectations +jsonPatch.invalidPathExpression=invalid path expression mergePatch.notContainer=value is neither an object or an array (found %s) diff --git a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java index 1e2d2c14..48feab05 100644 --- a/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java +++ b/src/test/java/com/github/fge/jsonpatch/JsonPathParserTest.java @@ -7,7 +7,7 @@ public class JsonPathParserTest { @Test - public void shouldConvertQueryToJsonPath() { + public void shouldConvertQueryToJsonPath() throws JsonPatchException { String jsonPointerWithQuery = "/productPrice/prodPriceAlteration?productPrice.name=Regular Price"; String expected = "$.productPrice[?(@.name=='Regular Price')].prodPriceAlteration"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -15,7 +15,7 @@ public void shouldConvertQueryToJsonPath() { } @Test - public void shouldConvertArrayPathToJsonPath() { + public void shouldConvertArrayPathToJsonPath() throws JsonPatchException { String jsonPointerWithQuery = "/2/1/-"; String expected = "$.[2].[1].-"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -23,7 +23,7 @@ public void shouldConvertArrayPathToJsonPath() { } @Test - public void shouldConvertBooleans() { + public void shouldConvertBooleans() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.valid=true&orderItem.product.relatedParty.role=customer"; String expected = "$.orderItem[?((@.productOffering.valid==true || @.productOffering.valid=='true') && @.product.relatedParty.role=='customer')].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -31,7 +31,7 @@ public void shouldConvertBooleans() { } @Test - public void shouldConvertFloatingPoint() { + public void shouldConvertFloatingPoint() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.price=1513.77&orderItem.product.relatedParty.role=customer"; String expected = "$.orderItem[?((@.productOffering.price==1513.77 || @.productOffering.price=='1513.77') && @.product.relatedParty.role=='customer')].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -39,7 +39,7 @@ public void shouldConvertFloatingPoint() { } @Test - public void shouldConvertIntegers() { + public void shouldConvertIntegers() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty.role=customer"; String expected = "$.orderItem[?((@.productOffering.id==1513 || @.productOffering.id=='1513') && @.product.relatedParty.role=='customer')].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -47,7 +47,7 @@ public void shouldConvertIntegers() { } @Test - public void shouldConvertManyConditions() { + public void shouldConvertManyConditions() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.product.relatedParty.role=customer&orderItem.product.relatedParty.name=Mary"; String expected = "$.orderItem[?(@.product.relatedParty.role=='customer' && @.product.relatedParty.name=='Mary')].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -55,7 +55,7 @@ public void shouldConvertManyConditions() { } @Test - public void shouldConvertNestedArrayQuery() { + public void shouldConvertNestedArrayQuery() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.productOffering.id=1513&orderItem.product.relatedParty[?(@.role=='customer' && @.name=='Mary')]"; String expected = "$.orderItem[?((@.productOffering.id==1513 || @.productOffering.id=='1513') && @.product.relatedParty[?(@.role=='customer' && @.name=='Mary')] empty false)].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -63,7 +63,7 @@ public void shouldConvertNestedArrayQuery() { } @Test - public void shouldConvertNestedArrayQueryWhichIsNotLastStatement() { + public void shouldConvertNestedArrayQueryWhichIsNotLastStatement() throws JsonPatchException { String jsonPointerWithQuery = "/orderItem/quantity?orderItem.product.relatedParty[?(@.role=='customer' && @.name=='Mary')]&orderItem.productOffering.id=1513"; String expected = "$.orderItem[?(@.product.relatedParty[?(@.role=='customer' && @.name=='Mary')] empty false && (@.productOffering.id==1513 || @.productOffering.id=='1513'))].quantity"; String result = JsonPathParser.tmfStringToJsonPath(jsonPointerWithQuery); @@ -71,7 +71,7 @@ public void shouldConvertNestedArrayQueryWhichIsNotLastStatement() { } @Test - public void shouldConvertFilterQuery() { + public void shouldConvertFilterQuery() throws JsonPatchException { String filterQuery = "note[?(@.author=='John Doe')].date"; String expected = "$.note[?(@.author=='John Doe')].date"; String result = JsonPathParser.tmfStringToJsonPath(filterQuery); diff --git a/src/test/resources/jsonpatch/query/add.json b/src/test/resources/jsonpatch/query/add.json index 63a4e6d8..e17efae2 100644 --- a/src/test/resources/jsonpatch/query/add.json +++ b/src/test/resources/jsonpatch/query/add.json @@ -157,6 +157,170 @@ } ] } + }, + { + "description": "Additional test 1", + "op": { + "op": "add", + "path": "/contactMedium/ThisHasBeenAdded?contactMedium.mediumType=phone", + "value": true + }, + "node": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + } + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + }, + "expected": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + }, + "ThisHasBeenAdded": true + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + } + }, + { + "description": "Additional test 2", + "op": { + "op": "add", + "path": "contactMedium[?(@.mediumType=='phone')].newProperty", + "value": { + "ThisHasBeenAdded": true + } + }, + "node": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + } + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + }, + "expected": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + }, + "newProperty": {"ThisHasBeenAdded": true} + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + } } ] } From c058eb15cef1552075350ab356ef769471c0f8f2 Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Wed, 2 Feb 2022 15:59:21 +0100 Subject: [PATCH 7/9] [US642] Improve naming - fix review comments --- .../github/fge/jsonpatch/AddOperation.java | 22 ++++++++++--------- .../github/fge/jsonpatch/JsonPathParser.java | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/fge/jsonpatch/AddOperation.java b/src/main/java/com/github/fge/jsonpatch/AddOperation.java index b5e9a30e..fbac00b3 100644 --- a/src/main/java/com/github/fge/jsonpatch/AddOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/AddOperation.java @@ -91,20 +91,22 @@ public JsonNode apply(final JsonNode node) throws JsonPatchException { final DocumentContext nodeContext = JsonPath.parse(node.deepCopy()); - final JsonNode parentNode = nodeContext.read(pathToParent); - if (parentNode == null) { + final JsonNode evaluatedJsonParents = nodeContext.read(pathToParent); + if (evaluatedJsonParents == null) { throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.noSuchParent")); } - if (!parentNode.isContainerNode()) { + if (!evaluatedJsonParents.isContainerNode()) { throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer")); } - // result will be a list of nodes - if (pathToParent.contains("[?(")) { - for (int i = 0; i < parentNode.size(); i++) { - JsonNode containerNode = parentNode.get(i); - DocumentContext containerContext = JsonPath.parse(containerNode); - if (containerNode.isArray()) { + if (pathToParent.contains("[?(")) { // json filter result is always a list + for (int i = 0; i < evaluatedJsonParents.size(); i++) { + JsonNode parentNode = evaluatedJsonParents.get(i); + if (!parentNode.isContainerNode()) { + throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.parentNotContainer")); + } + DocumentContext containerContext = JsonPath.parse(parentNode); + if (parentNode.isArray()) { addToArray(containerContext, "$", newNodeName); } else { addToObject(containerContext, "$", newNodeName); @@ -112,7 +114,7 @@ public JsonNode apply(final JsonNode node) throws JsonPatchException { } return nodeContext.read("$"); } else { - return parentNode.isArray() + return evaluatedJsonParents.isArray() ? addToArray(nodeContext, pathToParent, newNodeName) : addToObject(nodeContext, pathToParent, newNodeName); } diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java index 57fcc20e..4c6e2f7b 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPathParser.java @@ -15,8 +15,8 @@ public static String tmfStringToJsonPath(String path) throws JsonPatchException return "$"; } final String[] pointerAndQuery = path - .replaceAll("(\\w)\\?", "$1#") - .split("#", -1); + .replaceAll("(\\w)\\?", "$1#THIS_IS_SPLIT_PLACEHOLDER#") + .split("#THIS_IS_SPLIT_PLACEHOLDER#", -1); if (pointerAndQuery.length > 2) { throw new JsonPatchException(BUNDLE.getMessage("jsonPatch.invalidPathExpression")); } From bad9317787ffb6f7de655a249a2806351e97a5bc Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Wed, 2 Feb 2022 20:41:15 +0100 Subject: [PATCH 8/9] [US642] Additional tests for add operation --- src/test/resources/jsonpatch/query/add.json | 206 ++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/src/test/resources/jsonpatch/query/add.json b/src/test/resources/jsonpatch/query/add.json index e17efae2..a533f811 100644 --- a/src/test/resources/jsonpatch/query/add.json +++ b/src/test/resources/jsonpatch/query/add.json @@ -41,6 +41,129 @@ ] } }, + { + "op": { + "op": "add", + "path": "/note/text?note.verified=true|note.verified=false", + "value": "Informed" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + } + ] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "text": "Informed" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe", + "text": "Informed" + } + ] + } + }, + { + "op": { + "op": "add", + "path": "/note/text?note.verified=false", + "value": "Informed" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "text": "Old informed" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + } + ] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "text": "Informed" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + } + ] + } + }, + { + "op": { + "op": "add", + "path": "/note/text?note.text=Old informed", + "value": "Informed predicate" + }, + "node": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "text": "Old informed" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + } + ] + }, + "expected": { + "id": "1", + "correlationId": "TT53482", + "note": [ + { + "verified": false, + "date": "2013-07-24T09:55:30.0Z", + "author": "Arthur Evans", + "text": "Informed predicate" + }, + { + "verified": true, + "date": "2013-07-25T08:55:12.0Z", + "author": "John Doe" + } + ] + } + }, { "op": { "op": "add", @@ -239,6 +362,89 @@ ] } }, + { + "description": "Additional test 2", + "op": { + "op": "add", + "path": "contactMedium[?(@.mediumType=='phone')].newProperty", + "value": { + "ThisHasBeenAdded": true + } + }, + "node": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + } + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + }, + "expected": { + "aristocraticTitle": "Baron", + "boolean": true, + "deathDate": "2021-07-12T10:54:17Z", + "title": "Sir", + "contactMedium": [ + { + "mediumType": "phone", + "preferred": false, + "characteristic": { + "addressKeyType": "AA", + "addressKey": "AAAAA" + }, + "newProperty": {"ThisHasBeenAdded": true} + }, + { + "mediumType": "pager", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB" + } + }, + { + "mediumType": "facebook", + "preferred": true, + "characteristic": { + "addressKeyType": "BB", + "addressKey": "BBBBBB", + "detail": { + "detailA": "A", + "detailB": "B" + } + } + } + ] + } + }, { "description": "Additional test 2", "op": { From 0b6c1738a9e098893fee44384a4d20fae266f11c Mon Sep 17 00:00:00 2001 From: Piotr Dytkowski Date: Thu, 3 Feb 2022 09:08:33 +0100 Subject: [PATCH 9/9] [US642] Optimize imports --- src/main/java/com/github/fge/jsonpatch/AddOperation.java | 4 ---- src/main/java/com/github/fge/jsonpatch/DualPathOperation.java | 4 ++-- .../java/com/github/fge/jsonpatch/JsonPatchOperation.java | 1 - src/main/java/com/github/fge/jsonpatch/MoveOperation.java | 1 - .../java/com/github/fge/jsonpatch/PathValueOperation.java | 1 - src/main/java/com/github/fge/jsonpatch/ReplaceOperation.java | 3 --- 6 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/github/fge/jsonpatch/AddOperation.java b/src/main/java/com/github/fge/jsonpatch/AddOperation.java index fbac00b3..759fe5de 100644 --- a/src/main/java/com/github/fge/jsonpatch/AddOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/AddOperation.java @@ -22,11 +22,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.fge.jackson.jsonpointer.JsonPointer; -import com.github.fge.jackson.jsonpointer.ReferenceToken; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; diff --git a/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java b/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java index d26f7b8c..41d4a49d 100644 --- a/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/DualPathOperation.java @@ -51,8 +51,8 @@ protected DualPathOperation(final String op, final String from, final String pat public final void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); jgen.writeStringField("op", op); - jgen.writeStringField("path", path.toString()); - jgen.writeStringField("from", from.toString()); + jgen.writeStringField("path", path); + jgen.writeStringField("from", from); jgen.writeEndObject(); } diff --git a/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java b/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java index b0109ce2..de406eba 100644 --- a/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializable; -import com.github.fge.jackson.jsonpointer.JsonPointer; import com.github.fge.msgsimple.bundle.MessageBundle; import com.github.fge.msgsimple.load.MessageBundles; import com.jayway.jsonpath.Configuration; diff --git a/src/main/java/com/github/fge/jsonpatch/MoveOperation.java b/src/main/java/com/github/fge/jsonpatch/MoveOperation.java index 138214bc..df6850a9 100644 --- a/src/main/java/com/github/fge/jsonpatch/MoveOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/MoveOperation.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; -import com.github.fge.jackson.jsonpointer.JsonPointer; import com.jayway.jsonpath.JsonPath; /** diff --git a/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java b/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java index acfa12ab..e3dab45b 100644 --- a/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/PathValueOperation.java @@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; -import com.github.fge.jackson.jsonpointer.JsonPointer; import java.io.IOException; diff --git a/src/main/java/com/github/fge/jsonpatch/ReplaceOperation.java b/src/main/java/com/github/fge/jsonpatch/ReplaceOperation.java index 274fab37..c31a8076 100644 --- a/src/main/java/com/github/fge/jsonpatch/ReplaceOperation.java +++ b/src/main/java/com/github/fge/jsonpatch/ReplaceOperation.java @@ -22,9 +22,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.fge.jackson.jsonpointer.JsonPointer; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath;