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