You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by st...@apache.org on 2016/11/22 21:06:39 UTC

[4/6] johnzon git commit: Implemented JsonPointer

Implemented JsonPointer


Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/45faea0e
Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/45faea0e
Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/45faea0e

Branch: refs/heads/JSONP-1.1
Commit: 45faea0eb3e04f7adbe0724a448eb0cb62a4f258
Parents: d8a56d0
Author: Armin Hasler <ha...@gmail.com>
Authored: Wed Jul 6 17:06:26 2016 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Tue Nov 22 21:28:49 2016 +0100

----------------------------------------------------------------------
 .../org/apache/johnzon/core/JsonPointer.java    | 308 +++++++++++++++++++
 .../apache/johnzon/core/JsonPointerTest.java    | 197 ++++++++++++
 .../test/resources/json/jsonPointerTest.json    |  15 +
 3 files changed, 520 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/johnzon/blob/45faea0e/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointer.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointer.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointer.java
new file mode 100644
index 0000000..af228be
--- /dev/null
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonPointer.java
@@ -0,0 +1,308 @@
+/*
+ * 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 javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+/**
+ * <p>This class is an immutable representation of a JSON Pointer as specified in
+ * <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+ * </p>
+ * <p> A JSON Pointer, when applied to a target {@link JsonValue},
+ * defines a reference location in the target.</p>
+ * <p> An empty JSON Pointer string defines a reference to the target itself.</p>
+ * <p> If the JSON Pointer string is non-empty, it must be a sequence
+ * of '/' prefixed tokens, and the target must either be a {@link JsonArray}
+ * or {@link JsonObject}. If the target is a {@code JsonArray}, the pointer
+ * defines a reference to an array element, and the last token specifies the index.
+ * If the target is a {@link JsonObject}, the pointer defines a reference to a
+ * name/value pair, and the last token specifies the name.
+ * </p>
+ * <p> The method {@link JsonPointer#getValue getValue()} returns the referenced value.
+ * The methods {@link JsonPointer#add add()}, {@link JsonPointer#replace replace()},
+ * and {@link JsonPointer#remove remove()} executes the operations specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>. </p>
+ *
+ * @since 1.1
+ */
+
+public class JsonPointer {
+
+    private final String jsonPointer;
+    private final String[] referenceTokens;
+
+    /**
+     * Constructs and initializes a JsonPointer.
+     *
+     * @param jsonPointer the JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException        if {@code jsonPointer} is not a valid JSON Pointer
+     */
+    public JsonPointer(String jsonPointer) {
+        if (jsonPointer == null) {
+            throw new NullPointerException("jsonPointer must not be null");
+        }
+        if (!jsonPointer.equals("") && !jsonPointer.startsWith("/")) {
+            throw new JsonException("A non-empty JSON pointer must begin with a '/'");
+        }
+
+        this.jsonPointer = jsonPointer;
+        referenceTokens = jsonPointer.split("/", -1);
+    }
+
+    /**
+     * Compares this {@code JsonPointer} with another object.
+     *
+     * @param obj the object to compare this {@code JsonPointer} against
+     * @return true if the given object is a {@code JsonPointer} with the same
+     * reference tokens as this one, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        JsonPointer that = (JsonPointer) obj;
+        return jsonPointer.equals(that.jsonPointer);
+    }
+
+    /**
+     * Returns the hash code value for this {@code JsonPointer} object.
+     * The hash code of this object is defined by the hash codes of it's reference tokens.
+     *
+     * @return the hash code value for this {@code JsonPointer} object
+     */
+    @Override
+    public int hashCode() {
+        return jsonPointer.hashCode();
+    }
+
+    /**
+     * Returns the value at the referenced location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the referenced value in the target.
+     * @throws NullPointerException if {@code target} is null
+     * @throws JsonException        if the referenced value does not exist
+     */
+    public JsonValue getValue(JsonStructure target) {
+        if (target == null) {
+            throw new NullPointerException("target must not be null");
+        }
+
+        if (jsonPointer.equals("")) {
+            return target;
+        }
+
+        JsonValue jsonValue = target;
+
+        for (int i = 1; i < referenceTokens.length; i++) {
+            String decodedReferenceToken = JsonPointerUtil.decode(referenceTokens[i]);
+
+            if (jsonValue instanceof JsonObject) {
+                JsonObject jsonObject = (JsonObject) jsonValue;
+                jsonValue = jsonObject.get(decodedReferenceToken);
+
+                if (jsonValue == null) {
+                    throw new JsonException("The JsonObject " + jsonObject + " contains no value for token " + decodedReferenceToken);
+                }
+            } else if (jsonValue instanceof JsonArray) {
+                JsonArray jsonArray = (JsonArray) jsonValue;
+
+                try {
+                    int index = Integer.parseInt(decodedReferenceToken);
+                    if (index >= jsonArray.size()) {
+                        throw new JsonException("The JsonArray " + jsonArray + " contains no element for index " + index);
+                    }
+                    if (decodedReferenceToken.startsWith("0") && decodedReferenceToken.length() > 1) {
+                        throw new JsonException("The token " + decodedReferenceToken + " with leading zeros is not allowed to reference an element of a JsonArray");
+                    }
+
+                    jsonValue = jsonArray.get(index);
+                } catch (NumberFormatException e) {
+                    throw new JsonException("The token " + decodedReferenceToken + " for the JsonArray " + jsonArray + " is not a number", e);
+                }
+            }
+
+        }
+
+        return jsonValue;
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     * <ol>
+     * <li>If the reference is the target (empty JSON Pointer string),
+     * the specified {@code value}, which must be the same type as
+     * specified {@code target}, is returned.</li>
+     * <li>If the reference is an array element, the specified {@code value} is inserted
+     * into the array, at the referenced index. The value currently at that location, and
+     * any subsequent values, are shifted to the right (adds one to the indices).
+     * Index starts with 0. If the reference is specified with a "-", or if the
+     * index is equal to the size of the array, the value is appended to the array.</li>
+     * <li>If the reference is a name/value pair of a {@code JsonObject}, and the
+     * referenced value exists, the value is replaced by the specified {@code value}.
+     * If the value does not exist, a new name/value pair is added to the object.</li>
+     * </ol>
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     */
+    public JsonStructure add(JsonStructure target, JsonValue value) {
+        return null;
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure replace(JsonStructure target, JsonValue value) {
+        return null;
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure remove(JsonStructure target) {
+        return null;
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonObject add(JsonObject target, JsonValue value) {
+        return (JsonObject) this.add((JsonStructure) target, value);
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonArray add(JsonArray target, JsonValue value) {
+        return (JsonArray) this.add((JsonStructure) target, value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     *                              {@code target} with the specified {@code value}.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonObject replace(JsonObject target, JsonValue value) {
+        return (JsonObject) this.replace((JsonStructure) target, value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     *                              {@code target} with the specified {@code value}.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonArray replace(JsonArray target, JsonValue value) {
+        return (JsonArray) this.replace((JsonStructure) target, value);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonObject remove(JsonObject target) {
+        return (JsonObject) this.remove((JsonStructure) target);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonArray remove(JsonArray target) {
+        return (JsonArray) this.remove((JsonStructure) target);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/45faea0e/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPointerTest.java
----------------------------------------------------------------------
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPointerTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPointerTest.java
new file mode 100644
index 0000000..7750ba6
--- /dev/null
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonPointerTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 org.junit.Test;
+
+import javax.json.Json;
+import javax.json.JsonException;
+import javax.json.JsonReader;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonPointerTest {
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructorWithNullShouldThrowNullPointerException() {
+        new JsonPointer(null);
+    }
+
+    @Test(expected = JsonException.class)
+    public void testConstructorWithInvalidJsonPointerShouldThrowJsonException() {
+        new JsonPointer("a");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testGetValueWithNullShouldThrowNullPointerException() {
+        JsonPointer jsonPointer = new JsonPointer("");
+        jsonPointer.getValue(null);
+    }
+
+    @Test
+    public void testGetValueWholeDocument() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals(jsonDocument.toString(), result.toString());
+    }
+
+    @Test
+    public void testGetValue0() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("0", result.toString());
+    }
+
+    @Test
+    public void testGetValue1() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/a~1b");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("1", result.toString());
+    }
+
+    @Test
+    public void testGetValue2() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/c%d");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("2", result.toString());
+    }
+
+    @Test
+    public void testGetValue3() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/e^f");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("3", result.toString());
+    }
+
+    @Test
+    public void testGetValue4() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/g|h");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("4", result.toString());
+    }
+
+    @Test
+    public void testGetValue5() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/i\\j");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("5", result.toString());
+    }
+
+    @Test
+    public void testGetValue6() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/k\"l");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("6", result.toString());
+    }
+
+    @Test
+    public void testGetValue7() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/ ");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("7", result.toString());
+    }
+
+    @Test
+    public void testGetValue8() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/m~0n");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("8", result.toString());
+    }
+
+    @Test(expected = JsonException.class)
+    public void testGetValueElementNotExistentShouldThrowJsonException() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/fool");
+        jsonPointer.getValue(jsonDocument);
+    }
+
+    @Test
+    public void testGetValueWholeJsonArray() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/foo");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("[\"bar\",\"baz\"]", result.toString());
+    }
+
+    @Test
+    public void testGetValueJsonArrayElement() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/foo/0");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("\"bar\"", result.toString());
+    }
+
+    @Test(expected = JsonException.class)
+    public void testGetValueJsonArrayElementNotExistentShouldThrowJsonException() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/foo/2");
+        jsonPointer.getValue(jsonDocument);
+    }
+
+    @Test(expected = JsonException.class)
+    public void testGetValueJsonArrayElementNoNumberShouldThrowJsonException() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/foo/a");
+        jsonPointer.getValue(jsonDocument);
+    }
+
+    @Test(expected = JsonException.class)
+    public void testGetValueJsonArrayElementLeadingZeroShouldThrowJsonException() {
+        JsonStructure jsonDocument = getJsonDocument();
+
+        JsonPointer jsonPointer = new JsonPointer("/foo/001");
+        JsonValue result = jsonPointer.getValue(jsonDocument);
+        assertEquals("\"bar\"", result.toString());
+    }
+
+    private JsonStructure getJsonDocument() {
+        JsonReader reader = Json.createReaderFactory(Collections.<String, Object>emptyMap()).createReader(
+                Thread.currentThread().getContextClassLoader().getResourceAsStream("json/jsonPointerTest.json"));
+        return reader.read();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/45faea0e/johnzon-core/src/test/resources/json/jsonPointerTest.json
----------------------------------------------------------------------
diff --git a/johnzon-core/src/test/resources/json/jsonPointerTest.json b/johnzon-core/src/test/resources/json/jsonPointerTest.json
new file mode 100644
index 0000000..8cdc1c9
--- /dev/null
+++ b/johnzon-core/src/test/resources/json/jsonPointerTest.json
@@ -0,0 +1,15 @@
+{
+  "foo": [
+    "bar",
+    "baz"
+  ],
+  "": 0,
+  "a/b": 1,
+  "c%d": 2,
+  "e^f": 3,
+  "g|h": 4,
+  "i\\j": 5,
+  "k\"l": 6,
+  " ": 7,
+  "m~n": 8
+}
\ No newline at end of file