You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/09/08 16:15:24 UTC

[johnzon] branch master updated: JOHNZON-274 ensure JsonPatch respects requested path for arrays and objects

This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/master by this push:
     new e4d3684  JOHNZON-274 ensure JsonPatch respects requested path for arrays and objects
e4d3684 is described below

commit e4d3684269b37ece70143a68ebc50b346183307e
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Sun Sep 8 18:15:14 2019 +0200

    JOHNZON-274 ensure JsonPatch respects requested path for arrays and objects
---
 .../org/apache/johnzon/core/JsonPointerImpl.java   | 89 +++++++++++++---------
 .../org/apache/johnzon/core/JsonPatchTest.java     | 60 ++++++++++++---
 2 files changed, 103 insertions(+), 46 deletions(-)

diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
index 6cad8c2..89eed57 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
@@ -39,7 +39,6 @@ public class JsonPointerImpl implements JsonPointer {
     private final JsonProvider provider;
     private final String jsonPointer;
     private final List<String> referenceTokens = new ArrayList<>();
-    private final String lastReferenceToken;
     private static final Pattern IS_NUMBER = Pattern.compile("\\d+");
 
     /**
@@ -64,7 +63,6 @@ public class JsonPointerImpl implements JsonPointer {
         for (String encodedReferenceToken : encodedReferenceTokens) {
             referenceTokens.add(JsonPointerUtil.decode(encodedReferenceToken));
         }
-        lastReferenceToken = referenceTokens.get(referenceTokens.size() - 1);
     }
 
     /**
@@ -262,11 +260,10 @@ public class JsonPointerImpl implements JsonPointer {
      *                              or if the reference is the target.
      */
     public JsonStructure remove(JsonStructure target) {
-        if (target instanceof JsonObject) {
-            return remove((JsonObject) target);
-        } else {
-            return remove((JsonArray) target);
+        if (target.getValueType() == JsonValue.ValueType.OBJECT) {
+            return remove(target.asJsonObject());
         }
+        return remove(target.asJsonArray());
     }
 
     /**
@@ -282,7 +279,7 @@ public class JsonPointerImpl implements JsonPointer {
     public JsonObject remove(JsonObject target) {
         validateRemove(target);
 
-        return (JsonObject) remove(target, 1, referenceTokens.size() - 1);
+        return remove(target, 1).asJsonObject();
     }
 
     /**
@@ -298,13 +295,21 @@ public class JsonPointerImpl implements JsonPointer {
     public JsonArray remove(JsonArray target) {
         validateRemove(target);
 
-        return (JsonArray) remove(target, 1, referenceTokens.size() - 1);
+        return remove(target, 1).asJsonArray();
     }
 
     String getJsonPointer() {
         return jsonPointer;
     }
 
+    @Override
+    public String toString() {
+        return "JsonPointerImpl{" +
+                "jsonPointer='" + jsonPointer + '\'' +
+                ", referenceTokens=" + referenceTokens +
+                '}';
+    }
+
     private void validateAdd(JsonValue target) {
         validateJsonPointer(target, referenceTokens.size() - 1);
     }
@@ -356,12 +361,12 @@ public class JsonPointerImpl implements JsonPointer {
     }
 
     private JsonValue addInternal(JsonValue jsonValue, JsonValue newValue, List<String> currentPath) {
-        if (jsonValue instanceof JsonObject) {
+        if (jsonValue.getValueType() == JsonValue.ValueType.OBJECT) {
             JsonObject jsonObject = (JsonObject) jsonValue;
             JsonObjectBuilder objectBuilder = provider.createObjectBuilder();
 
             if (jsonObject.isEmpty() && isPositionToAdd(currentPath)) {
-                objectBuilder.add(lastReferenceToken, newValue);
+                objectBuilder.add(referenceTokens.get(referenceTokens.size() - 1), newValue);
             } else {
                 for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
 
@@ -370,18 +375,18 @@ public class JsonPointerImpl implements JsonPointer {
                     currentPath.remove(entry.getKey());
 
                     if (isPositionToAdd(currentPath)) {
-                        objectBuilder.add(lastReferenceToken, newValue);
+                        objectBuilder.add(referenceTokens.get(referenceTokens.size() - 1), newValue);
                     }
                 }
             }
             return objectBuilder.build();
-        } else if (jsonValue instanceof JsonArray) {
+        } else if (jsonValue.getValueType() == JsonValue.ValueType.ARRAY) {
             JsonArray jsonArray = (JsonArray) jsonValue;
             JsonArrayBuilder arrayBuilder = provider.createArrayBuilder();
 
             int arrayIndex = -1;
             if (isPositionToAdd(currentPath)) {
-                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, true);
+                arrayIndex = getArrayIndex(referenceTokens.get(referenceTokens.size() - 1), jsonArray, true);
             }
 
             int jsonArraySize = jsonArray.size();
@@ -408,36 +413,46 @@ public class JsonPointerImpl implements JsonPointer {
                 currentPath.get(currentPath.size() - 1).equals(referenceTokens.get(referenceTokens.size() - 2));
     }
 
-    private JsonValue remove(JsonValue jsonValue, int currentPosition, int referencePosition) {
-        if (jsonValue instanceof JsonObject) {
-            JsonObject jsonObject = (JsonObject) jsonValue;
-            JsonObjectBuilder objectBuilder = provider.createObjectBuilder();
+    private JsonValue remove(final JsonValue jsonValue, final int currentPosition) {
+        if (referenceTokens.size() <= currentPosition) { // unlikely
+            return jsonValue;
+        }
 
-            for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
-                if (currentPosition == referencePosition
-                        && lastReferenceToken.equals(entry.getKey())) {
+        final String token = referenceTokens.get(currentPosition);
+        if (jsonValue.getValueType() == JsonValue.ValueType.OBJECT) {
+            final JsonObject jsonObject = jsonValue.asJsonObject();
+            final JsonObjectBuilder objectBuilder = provider.createObjectBuilder();
+            for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
+                final boolean matchesToken = token.equals(entry.getKey());
+                if (matchesToken && currentPosition == referenceTokens.size() - 1) {
                     continue;
                 }
-                objectBuilder.add(entry.getKey(), remove(entry.getValue(), currentPosition + 1, referencePosition));
+                if (matchesToken) {
+                    objectBuilder.add(
+                            entry.getKey(),
+                            remove(entry.getValue(), currentPosition + 1));
+                } else {
+                    objectBuilder.add(entry.getKey(), entry.getValue());
+                }
             }
             return objectBuilder.build();
-        } else if (jsonValue instanceof JsonArray) {
-            JsonArray jsonArray = (JsonArray) jsonValue;
-            JsonArrayBuilder arrayBuilder = provider.createArrayBuilder();
-
-            int arrayIndex = -1;
-            if (currentPosition == referencePosition && IS_NUMBER.matcher(lastReferenceToken).matches()) {
-                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, false);
-            }
-
-            int jsonArraySize = jsonArray.size();
-            for (int i = 0; i < jsonArraySize; i++) {
-                if (i == arrayIndex) {
-                    continue;
+        } else if (jsonValue.getValueType() == JsonValue.ValueType.ARRAY) {
+            final JsonArray jsonArray = jsonValue.asJsonArray();
+            if (IS_NUMBER.matcher(token).matches()) {
+                final JsonArrayBuilder arrayBuilder = provider.createArrayBuilder();
+                final int arrayIndex = getArrayIndex(token, jsonArray, false);
+                final int jsonArraySize = jsonArray.size();
+                for (int i = 0; i < jsonArraySize; i++) {
+                    final boolean matchesIndex = i == arrayIndex;
+                    if (matchesIndex && currentPosition != referenceTokens.size() - 1) {
+                        arrayBuilder.add(remove(jsonArray.get(i), currentPosition + 1));
+                    } else if (!matchesIndex) {
+                        arrayBuilder.add(jsonArray.get(i));
+                    }
                 }
-                arrayBuilder.add(remove(jsonArray.get(i), currentPosition + 1, referencePosition));
-            }
-            return arrayBuilder.build();
+                return arrayBuilder.build();
+            } // else?
+            return jsonArray;
         }
         return jsonValue;
     }
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchTest.java
index e579e5b..6d9b41e 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchTest.java
@@ -29,12 +29,9 @@ import javax.json.JsonObject;
 import javax.json.JsonPatch;
 import javax.json.JsonStructure;
 import javax.json.JsonValue;
-import javax.json.JsonWriter;
 import javax.json.spi.JsonProvider;
 
 import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -998,15 +995,60 @@ public class JsonPatchTest {
         Assert.assertNotNull(patchedJsonObject);
     }
 
-
-    private static String toJsonString(JsonStructure value) {
-        final Writer writer = new StringWriter();
-        try (final JsonWriter jsonWriter = Json.createWriter(writer)) {
-            jsonWriter.write(value);
+    @Test
+    public void testReplacingObjectAttribute() {
+        final JsonObject object = Json.createObjectBuilder()
+            .add("foo", Json.createObjectBuilder()
+                    .add("baz", Json.createValue("1")))
+            .add("bar", Json.createObjectBuilder()
+                    .add("baz", Json.createValue("2")))
+            .build();
+        final JsonPatchImpl patch = new JsonPatchImpl(
+            PROVIDER, new JsonPatchImpl.PatchValue(PROVIDER, JsonPatch.Operation.REPLACE,
+                "/bar/baz",
+                null,
+                Json.createValue("3")));
+        final JsonObject patched = patch.apply(object);
+        assertNotNull(patched);
+        assertNotSame(object, patched);
+        {
+            final JsonObject o = patched.getJsonObject("foo");
+            assertNotNull(o);
+            assertEquals(Json.createValue("1"), o.getJsonString("baz"));
+        }
+        {
+            final JsonObject o = patched.getJsonObject("bar");
+            assertNotNull(o);
+            assertEquals(Json.createValue("3"), o.getJsonString("baz"));
+            assertEquals("{\"foo\":{\"baz\":\"1\"},\"bar\":{\"baz\":\"3\"}}", toJsonString(patched));
         }
-        return writer.toString();
     }
 
+    @Test
+    public void testReplacingArrayElementAttribute() {
+        final JsonObject object = Json.createObjectBuilder()
+            .add("foo", Json.createArrayBuilder()
+                    .add(Json.createObjectBuilder().add("bar", Json.createValue("1")))
+                    .add(Json.createObjectBuilder().add("bar", Json.createValue("2"))))
+            .build();
+        final JsonPatchImpl patch = new JsonPatchImpl(PROVIDER, new JsonPatchImpl.PatchValue(PROVIDER, JsonPatch.Operation.REPLACE,
+            "/foo/1/bar",
+            null,
+            Json.createValue("3")));
+        final JsonObject patched = patch.apply(object);
+        assertNotNull(patched);
+        assertNotSame(object, patched);
+        final JsonArray array = patched.getJsonArray("foo");
+        assertNotNull(array);
+        assertNotSame(object.getJsonArray("foo"), array);
+        assertEquals(2, array.size());
+        assertEquals(Json.createValue("3"), array.getJsonObject(1).getJsonString("bar"));
+        assertEquals(Json.createValue("1"), array.getJsonObject(0).getJsonString("bar"));
+        assertEquals("{\"foo\":[{\"bar\":\"1\"},{\"bar\":\"3\"}]}", toJsonString(patched));
+    }
 
 
+    private static String toJsonString(final JsonStructure value) {
+        return value.toString();
+    }
 }