Repository: johnzon Updated Branches: refs/heads/master 61a130e3e -> 2940f6e13
JOHNZON-103 add tests and basic impl for JsonPatch Diff handling Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/2940f6e1 Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/2940f6e1 Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/2940f6e1 Branch: refs/heads/master Commit: 2940f6e1309080f37a70ac538275d80b9d5abe19 Parents: 61a130e Author: Mark Struberg <[email protected]> Authored: Mon Feb 27 21:34:34 2017 +0100 Committer: Mark Struberg <[email protected]> Committed: Mon Feb 27 21:34:34 2017 +0100 ---------------------------------------------------------------------- .../org/apache/johnzon/core/JsonPatchDiff.java | 88 ++++++++++++++++++ .../apache/johnzon/core/JsonProviderImpl.java | 5 ++ .../johnzon/core/JsonMergeBatchBuilderTest.java | 20 ++--- .../apache/johnzon/core/JsonPatchDiffTest.java | 93 ++++++++++++++++++++ .../org/apache/johnzon/core/JsonPatchTest.java | 39 -------- 5 files changed, 196 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/2940f6e1/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java new file mode 100644 index 0000000..81a7bf2 --- /dev/null +++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java @@ -0,0 +1,88 @@ +/* + * 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.johnzon.core; + +import java.util.Map; +import java.util.Set; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonPatch; +import javax.json.JsonPatchBuilder; +import javax.json.JsonStructure; +import javax.json.JsonValue; + +/** + * Create a diff from a source and target JsonStructure + */ +public class JsonPatchDiff { + + private final JsonStructure source; + private final JsonStructure target; + + public JsonPatchDiff(JsonStructure source, JsonStructure target) { + this.source = source; + this.target = target; + } + + public JsonPatch calculateDiff() { + JsonPatchBuilder patchBuilder = new JsonPatchBuilderImpl(); + + diff(patchBuilder, "/", source, target); + + return patchBuilder.build(); + } + + private void diff(JsonPatchBuilder patchBuilder, String basePath, JsonStructure source, JsonStructure target) { + if (source instanceof JsonObject && target instanceof JsonObject) { + // handle JsonObjects + diffJsonObjects(patchBuilder, basePath, (JsonObject) source, (JsonObject) target); + } else if (source instanceof JsonArray && target instanceof JsonArray) { + // handle JsonArray + //X TODO + throw new UnsupportedOperationException("not yet implemented."); + } else { + throw new UnsupportedOperationException("not yet implemented."); + } + } + + private void diffJsonObjects(JsonPatchBuilder patchBuilder, String basePath, JsonObject source, JsonObject target) { + Set<Map.Entry<String, JsonValue>> sourceEntries = source.entrySet(); + + for (Map.Entry<String, JsonValue> sourceEntry : sourceEntries) { + String attributeName = sourceEntry.getKey(); + if (target.containsKey(attributeName)) { + JsonValue targetValue = ((JsonObject) target).get(attributeName); + if (!sourceEntry.getValue().equals(targetValue)) { + // replace the original value + patchBuilder.replace(basePath + attributeName, targetValue); + } + } else { + // the value got removed + patchBuilder.remove(basePath + attributeName); + } + } + + Set<Map.Entry<String, JsonValue>> targetEntries = target.entrySet(); + for (Map.Entry<String, JsonValue> targetEntry : targetEntries) { + if (!source.containsKey(targetEntry.getKey())) { + patchBuilder.add(basePath + targetEntry.getKey(), targetEntry.getValue()); + } + } + + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/2940f6e1/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java index 354158a..9df37da 100644 --- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java +++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java @@ -364,6 +364,11 @@ public class JsonProviderImpl extends JsonProvider implements Serializable { return createPatchBuilder(array).build(); } + @Override + public JsonPatch createDiff(JsonStructure source, JsonStructure target) { + return new JsonPatchDiff(source, target).calculateDiff(); + } + //X TODO add missing methods } http://git-wip-us.apache.org/repos/asf/johnzon/blob/2940f6e1/johnzon-core/src/test/java/org/apache/johnzon/core/JsonMergeBatchBuilderTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonMergeBatchBuilderTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonMergeBatchBuilderTest.java index 141ed9d..b5be087 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonMergeBatchBuilderTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonMergeBatchBuilderTest.java @@ -16,20 +16,20 @@ */ package org.apache.johnzon.core; -import javax.json.spi.JsonProvider; -public class JsonMergeBatchBuilderTest { +import org.junit.Test; - private static final String[][] TEST_CASES = new String[][]{ - { - "{\"a\":\"b\"}", - "{\"a\":\"c\"}", - "{\"a\":\"c\"}" - } - }; +public class JsonMergeBatchBuilderTest { + @Test public void testSimpleMergePatch() { - JsonProvider provider = JsonProvider.provider(); + // {"a":"xa","b","xb"} + String source = "{\"a\":\"xa\",\"b\",\"xb\"}"; + + // {"b":"bNew","c":"xc"} + String patch = "{\"b\":\"bNew\",\"c\":\"xc\"}"; + + //X TODO Json.createMergePatch(); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/2940f6e1/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchDiffTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchDiffTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchDiffTest.java new file mode 100644 index 0000000..3462a19 --- /dev/null +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchDiffTest.java @@ -0,0 +1,93 @@ +/* + * 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.johnzon.core; + +import java.io.StringReader; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonPatch; +import javax.json.JsonValue; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +public class JsonPatchDiffTest { + + @Test + public void testAddDiff() { + // {"a":"xa"} + String jsonA = "{\"a\":\"xa\"}"; + + // {"a":"xa","b":"xb"} + String jsonB = "{\"a\":\"xa\",\"b\":\"xb\"}"; + + // this results in 1 diff operations: + // adding "b" + JsonPatch jsonPatch = Json.createDiff(Json.createReader(new StringReader(jsonA)).readObject(), + Json.createReader(new StringReader(jsonB)).readObject()); + Assert.assertNotNull(jsonPatch); + JsonArray patchOperations = jsonPatch.toJsonArray(); + Assert.assertNotNull(patchOperations); + Assert.assertEquals(1, patchOperations.size()); + containsOperation(patchOperations, JsonPatch.Operation.ADD, "/b", "xb"); + } + + @Test + @Ignore //X TODO reinhard take over ;) + public void testComplexDiff() { + // {"a":"xa","b":2,"c":{"d":"xd"},"e":[1,2,3]} + String jsonA = "{\"a\":\"xa\",\"b\":2,\"c\":{\"d\":\"xd\"},\"e\":[1,2,3]}"; + + // {"a":"xa","c":{"d":"xd", "d2":"xd2"},"e":[1,3],"f":"xe"} + String jsonB = "{\"a\":\"xa\",\"c\":{\"d\":\"xd\", \"d2\":\"xd2\"},\"e\":[1,3],\"f\":\"xe\"}"; + + // this results in 4 diff operations: + // removing b, adding d2, removing 2 from e, adding f + JsonPatch jsonPatch = Json.createDiff(Json.createReader(new StringReader(jsonA)).readObject(), + Json.createReader(new StringReader(jsonB)).readObject()); + Assert.assertNotNull(jsonPatch); + JsonArray patchOperations = jsonPatch.toJsonArray(); + Assert.assertNotNull(patchOperations); + Assert.assertEquals(4, patchOperations.size()); + containsOperation(patchOperations, JsonPatch.Operation.REMOVE, "/b", null); + containsOperation(patchOperations, JsonPatch.Operation.ADD, "/c/d2", "xd2"); + containsOperation(patchOperations, JsonPatch.Operation.REMOVE, "/e/2", null); + containsOperation(patchOperations, JsonPatch.Operation.ADD, "/f", "xe"); + } + + private void containsOperation(JsonArray patchOperations, JsonPatch.Operation patchOperation, + String jsonPointer, String value) { + for (JsonValue operation : patchOperations) { + if (operation instanceof JsonObject && + patchOperation.operationName().equalsIgnoreCase(((JsonObject) operation).getString("op"))) { + Assert.assertEquals(jsonPointer, ((JsonObject) operation).getString("path")); + + if (value != null) { + Assert.assertEquals(value, ((JsonObject) operation).getString("value")); + } + + return; + } + } + Assert.fail("patchOperations does not contain " + patchOperation + " " + jsonPointer); + } + + +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/2940f6e1/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPatchTest.java ---------------------------------------------------------------------- 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 dc54c0c..6c96329 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 @@ -969,45 +969,6 @@ public class JsonPatchTest { } - //X @Test disabled for now - //X TODO implement the diff logic! - public void testDiff() { - // {"a":"xa","b":2,"c":{"d":"xd"},"e":[1,2,3]} - String jsonA = "{\"a\":\"xa\",\"b\":2,\"c\":{\"d\":\"xd\"},\"e\":[1,2,3]}"; - - // {"a":"xa","c":{"d":"xd", "d2":"xd2"},"e":[1,3],"f":"xe"} - String jsonB = "{\"a\":\"xa\",\"c\":{\"d\":\"xd\", \"d2\":\"xd2\"},\"e\":[1,3],\"f\":\"xe\"}"; - - // this results in 4 diff operations: - // removing b, adding d2, removing 2 from e, adding f - JsonPatch jsonPatch = Json.createDiff(Json.createReader(new StringReader(jsonA)).readObject(), - Json.createReader(new StringReader(jsonB)).readObject()); - Assert.assertNotNull(jsonPatch); - JsonArray patchOperations = jsonPatch.toJsonArray(); - Assert.assertNotNull(patchOperations); - Assert.assertEquals(4, patchOperations.size()); - containsOperation(patchOperations, JsonPatch.Operation.REMOVE, "/b", null); - containsOperation(patchOperations, JsonPatch.Operation.ADD, "/c/d2", "xd2"); - containsOperation(patchOperations, JsonPatch.Operation.REMOVE, "/e/2", null); - containsOperation(patchOperations, JsonPatch.Operation.ADD, "/f", "xe"); - } - - private void containsOperation(JsonArray patchOperations, JsonPatch.Operation patchOperation, - String jsonPointer, String value) { - for (JsonValue operation : patchOperations) { - if (operation instanceof JsonObject && - patchOperation.operationName().equalsIgnoreCase(((JsonObject) operation).getString("op"))) { - Assert.assertEquals(jsonPointer, ((JsonObject) operation).getString("path")); - - if (value != null) { - Assert.assertEquals(value, ((JsonObject) operation).getString("value")); - } - } - } - Assert.fail("patchOperations does not contain " + patchOperation + " " + jsonPointer); - } - - private static String toJsonString(JsonStructure value) { StringWriter writer = new StringWriter(); Json.createWriter(writer).write(value);
