remove JSON parser, use a smaller JSON builder for JSON writing, add creation/modification date methods to MarcRecord
This commit is contained in:
parent
09db520c2e
commit
3c5b4bee3d
31 changed files with 399 additions and 6372 deletions
|
@ -47,11 +47,3 @@ https://github.com/marc4j/marc4j/tree/master/test/resources
|
||||||
and were donated by libraries for testing purpose.
|
and were donated by libraries for testing purpose.
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The JSON reader/writer classes are derived work from
|
|
||||||
|
|
||||||
https://github.com/ralfstx/minimal-json
|
|
||||||
|
|
||||||
The original work is based on the MIT License
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
|
|
|
@ -187,6 +187,28 @@ public class MarcField implements Comparable<MarcField> {
|
||||||
return builder.value;
|
return builder.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a "top most" value, useful for recovering a control field value from
|
||||||
|
* hierarchical structures like JSON formats.
|
||||||
|
* @return the value
|
||||||
|
*/
|
||||||
|
public String recoverControlFieldValue() {
|
||||||
|
String value = getValue();
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
// the control field is disguised as a data field, try lookup value in first subfield of "_"
|
||||||
|
value = getFirstSubfieldValue("_");
|
||||||
|
// if no value, maybe blank " "?
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
value = getFirstSubfieldValue(" ");
|
||||||
|
}
|
||||||
|
// still no value? Then it is some exotic like MAB with subfield "a"?
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
value = getFirstSubfieldValue("a");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this MARC field is a control field.
|
* Returns if this MARC field is a control field.
|
||||||
* @return true if control field, false if not
|
* @return true if control field, false if not
|
||||||
|
|
|
@ -174,7 +174,7 @@ public class MarcRecord implements Map<String, Object> {
|
||||||
|
|
||||||
public LocalDate getCreationDate() {
|
public LocalDate getCreationDate() {
|
||||||
if (marcFields != null) {
|
if (marcFields != null) {
|
||||||
String value = getFirst("008").getValue();
|
String value = getFirst("008").recoverControlFieldValue();
|
||||||
if (value != null && value.length() >= 6) {
|
if (value != null && value.length() >= 6) {
|
||||||
return LocalDate.parse(value.substring(0, 6), field8DateFormat);
|
return LocalDate.parse(value.substring(0, 6), field8DateFormat);
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ public class MarcRecord implements Map<String, Object> {
|
||||||
|
|
||||||
public LocalDate getLastModificationDate() {
|
public LocalDate getLastModificationDate() {
|
||||||
if (marcFields != null) {
|
if (marcFields != null) {
|
||||||
String value = getFirst("005").getValue();
|
String value = getFirst("005").recoverControlFieldValue();
|
||||||
if (value != null && value.length() >= 8) {
|
if (value != null && value.length() >= 8) {
|
||||||
return LocalDate.parse(value.substring(0, 8), field5DateFormat);
|
return LocalDate.parse(value.substring(0, 8), field5DateFormat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,279 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class serves as the entry point to the JSON API.
|
|
||||||
* <p>
|
|
||||||
* To <strong>parse</strong> a given JSON input, use the <code>parse()</code> methods like in this
|
|
||||||
* example:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* JsonObject object = Json.parse(string).asObject();
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* To <strong>create</strong> a JSON data structure to be serialized, use the methods
|
|
||||||
* <code>value()</code>, <code>array()</code>, and <code>object()</code>. For example, the following
|
|
||||||
* snippet will produce the JSON string <em>{"foo": 23, "bar": true}</em>:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* String string = Json.object().add("foo", 23).add("bar", true).toString();
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* To create a JSON array from a given Java array, you can use one of the <code>array()</code>
|
|
||||||
* methods with varargs parameters:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* String[] names = ...
|
|
||||||
* JsonArray array = Json.array(names);
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public final class Json {
|
|
||||||
|
|
||||||
private Json() {
|
|
||||||
// not meant to be instantiated
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given <code>int</code> value.
|
|
||||||
*
|
|
||||||
* @param value the value to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given value
|
|
||||||
*/
|
|
||||||
public static JsonValue of(int value) {
|
|
||||||
return new JsonNumber(Integer.toString(value, 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given <code>long</code> value.
|
|
||||||
*
|
|
||||||
* @param value the value to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given value
|
|
||||||
*/
|
|
||||||
public static JsonValue of(long value) {
|
|
||||||
return new JsonNumber(Long.toString(value, 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given <code>float</code> value.
|
|
||||||
*
|
|
||||||
* @param value the value to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given value
|
|
||||||
*/
|
|
||||||
public static JsonValue of(float value) {
|
|
||||||
if (Float.isInfinite(value) || Float.isNaN(value)) {
|
|
||||||
throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
|
|
||||||
}
|
|
||||||
return new JsonNumber(cutOffPointZero(Float.toString(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given <code>double</code> value.
|
|
||||||
*
|
|
||||||
* @param value the value to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given value
|
|
||||||
*/
|
|
||||||
public static JsonValue of(double value) {
|
|
||||||
if (Double.isInfinite(value) || Double.isNaN(value)) {
|
|
||||||
throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON");
|
|
||||||
}
|
|
||||||
return new JsonNumber(cutOffPointZero(Double.toString(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given string.
|
|
||||||
*
|
|
||||||
* @param string the string to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given string
|
|
||||||
*/
|
|
||||||
public static JsonValue of(String string) {
|
|
||||||
return string == null ? JsonLiteral.NULL : new JsonString(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JsonValue instance that represents the given <code>boolean</code> value.
|
|
||||||
*
|
|
||||||
* @param value the value to get a JSON representation for
|
|
||||||
* @return a JSON value that represents the given value
|
|
||||||
*/
|
|
||||||
public static JsonValue of(boolean value) {
|
|
||||||
return value ? JsonLiteral.TRUE : JsonLiteral.FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the
|
|
||||||
* constructor.
|
|
||||||
*
|
|
||||||
* @return a new empty JSON array
|
|
||||||
*/
|
|
||||||
public static JsonArray array() {
|
|
||||||
return new JsonArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given <code>int</code>
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @param values the values to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given values
|
|
||||||
*/
|
|
||||||
public static JsonArray array(int... values) {
|
|
||||||
Objects.requireNonNull(values);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given <code>long</code>
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @param values the values to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given values
|
|
||||||
*/
|
|
||||||
public static JsonArray array(long... values) {
|
|
||||||
Objects.requireNonNull(values);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (long value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given <code>float</code>
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @param values the values to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given values
|
|
||||||
*/
|
|
||||||
public static JsonArray array(float... values) {
|
|
||||||
Objects.requireNonNull(values);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (float value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given <code>double</code>
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @param values the values to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given values
|
|
||||||
*/
|
|
||||||
public static JsonArray array(double... values) {
|
|
||||||
Objects.requireNonNull(values);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (double value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given
|
|
||||||
* <code>boolean</code> values.
|
|
||||||
*
|
|
||||||
* @param values the values to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given values
|
|
||||||
*/
|
|
||||||
public static JsonArray array(boolean... values) {
|
|
||||||
Objects.requireNonNull(values);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (boolean value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray that contains the JSON representations of the given strings.
|
|
||||||
*
|
|
||||||
* @param strings the strings to be included in the new JSON array
|
|
||||||
* @return a new JSON array that contains the given strings
|
|
||||||
*/
|
|
||||||
public static JsonArray array(String... strings) {
|
|
||||||
Objects.requireNonNull(strings);
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (String value : strings) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the
|
|
||||||
* constructor.
|
|
||||||
*
|
|
||||||
* @return a new empty JSON object
|
|
||||||
*/
|
|
||||||
public static JsonObject object() {
|
|
||||||
return new JsonObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the given input string as JSON. The input must contain a valid JSON value, optionally
|
|
||||||
* padded with whitespace.
|
|
||||||
*
|
|
||||||
* @param string the input string, must be valid JSON
|
|
||||||
* @return a value that represents the parsed JSON
|
|
||||||
* @throws IOException if the input is not valid JSON
|
|
||||||
*/
|
|
||||||
public static JsonValue parse(String string) throws IOException {
|
|
||||||
Objects.requireNonNull(string);
|
|
||||||
JsonDefaultHandler handler = new JsonDefaultHandler();
|
|
||||||
new JsonReader<>(new StringReader(string), handler).parse();
|
|
||||||
return handler.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entire input from the given reader and parses it as JSON. The input must contain a
|
|
||||||
* valid JSON value, optionally padded with whitespace.
|
|
||||||
* <p>
|
|
||||||
* Characters are read in chunks into an input buffer. Hence, wrapping a reader in an additional
|
|
||||||
* <code>BufferedReader</code> likely won't improve reading performance.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param reader the reader to read the JSON value from
|
|
||||||
* @return a value that represents the parsed JSON
|
|
||||||
* @throws IOException if an I/O error occurs in the reader
|
|
||||||
* @throws JsonException if the input is not valid JSON
|
|
||||||
*/
|
|
||||||
public static JsonValue parse(Reader reader) throws IOException {
|
|
||||||
JsonDefaultHandler handler = new JsonDefaultHandler();
|
|
||||||
try (reader) {
|
|
||||||
new JsonReader<>(reader, handler).parse();
|
|
||||||
}
|
|
||||||
return handler.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String cutOffPointZero(String string) {
|
|
||||||
if (string.endsWith(".0")) {
|
|
||||||
return string.substring(0, string.length() - 2);
|
|
||||||
}
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,397 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a JSON array, an ordered collection of JSON values.
|
|
||||||
* <p>
|
|
||||||
* Elements can be added using the <code>add(...)</code> methods which accept instances of
|
|
||||||
* {@link JsonValue}, strings, primitive numbers, and boolean values. To replace an element of an
|
|
||||||
* array, use the <code>set(int, ...)</code> methods.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Elements can be accessed by their index using {@link #get(int)}. This class also supports
|
|
||||||
* iterating over the elements in document order using an {@link #iterator()} or an enhanced for
|
|
||||||
* loop:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* for (JsonValue value : jsonArray) {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* An equivalent {@link List} can be obtained from the method {@link #values()}.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Note that this class is <strong>not thread-safe</strong>. If multiple threads access a
|
|
||||||
* <code>JsonArray</code> instance concurrently, while at least one of these threads modifies the
|
|
||||||
* contents of this array, access to the instance must be synchronized externally. Failure to do so
|
|
||||||
* may lead to an inconsistent state.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class JsonArray extends JsonValue implements Iterable<JsonValue> {
|
|
||||||
|
|
||||||
private final List<JsonValue> values;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new empty JsonArray.
|
|
||||||
*/
|
|
||||||
public JsonArray() {
|
|
||||||
values = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonArray with the contents of the specified JSON array.
|
|
||||||
*
|
|
||||||
* @param array the JsonArray to get the initial contents from, must not be <code>null</code>
|
|
||||||
*/
|
|
||||||
public JsonArray(JsonArray array) {
|
|
||||||
Objects.requireNonNull(array);
|
|
||||||
values = new ArrayList<>(array.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified <code>int</code> value to the end of this
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* @param value the value to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(int value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified <code>long</code> value to the end of this
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* @param value the value to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(long value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified <code>float</code> value to the end of this
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* @param value the value to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(float value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified <code>double</code> value to the end of this
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* @param value the value to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(double value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified <code>boolean</code> value to the end of this
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* @param value the value to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(boolean value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the JSON representation of the specified string to the end of this array.
|
|
||||||
*
|
|
||||||
* @param value the string to add to the array
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(String value) {
|
|
||||||
values.add(Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends the specified JSON value to the end of this array.
|
|
||||||
*
|
|
||||||
* @param value the JsonValue to add to the array, must not be <code>null</code>
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonArray add(JsonValue value) {
|
|
||||||
Objects.requireNonNull(value);
|
|
||||||
values.add(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified <code>int</code> value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, int value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified <code>long</code> value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, long value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified <code>float</code> value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, float value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified <code>double</code> value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, double value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified <code>boolean</code> value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, boolean value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the JSON representation of
|
|
||||||
* the specified string.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the string to be stored at the specified array position
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, String value) {
|
|
||||||
values.set(index, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the element at the specified position in this array with the specified JSON value.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to replace
|
|
||||||
* @param value the value to be stored at the specified array position, must not be <code>null</code>
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray set(int index, JsonValue value) {
|
|
||||||
if (value == null) {
|
|
||||||
throw new NullPointerException();
|
|
||||||
}
|
|
||||||
values.set(index, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the element at the specified index from this array.
|
|
||||||
*
|
|
||||||
* @param index the index of the element to remove
|
|
||||||
* @return the array itself, to enable method chaining
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonArray remove(int index) {
|
|
||||||
values.remove(index);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of elements in this array.
|
|
||||||
*
|
|
||||||
* @return the number of elements in this array
|
|
||||||
*/
|
|
||||||
public int size() {
|
|
||||||
return values.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns <code>true</code> if this array contains no elements.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this array contains no elements
|
|
||||||
*/
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return values.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the element at the specified position in this array.
|
|
||||||
*
|
|
||||||
* @param index the index of the array element to return
|
|
||||||
* @return the value of the element at the specified position
|
|
||||||
* @throws IndexOutOfBoundsException if the index is out of range, i.e. <code>index < 0</code> or
|
|
||||||
* <code>index >= size</code>
|
|
||||||
*/
|
|
||||||
public JsonValue get(int index) {
|
|
||||||
return values.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of the values in this array in document order.
|
|
||||||
*
|
|
||||||
* @return a list of the values in this array
|
|
||||||
*/
|
|
||||||
public List<JsonValue> values() {
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an iterator over the values of this array in document order. The returned iterator
|
|
||||||
* cannot be used to modify this array.
|
|
||||||
*
|
|
||||||
* @return an iterator over the values of this array
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<JsonValue> iterator() {
|
|
||||||
final Iterator<JsonValue> iterator = values.iterator();
|
|
||||||
return new Iterator<>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return iterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonValue next() {
|
|
||||||
return iterator.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(JsonWriter writer) throws IOException {
|
|
||||||
writer.writeArrayOpen();
|
|
||||||
Iterator<JsonValue> iterator = iterator();
|
|
||||||
if (iterator.hasNext()) {
|
|
||||||
iterator.next().write(writer);
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
writer.writeArraySeparator();
|
|
||||||
iterator.next().write(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.writeArrayClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isArray() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonArray asArray() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return values.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether a given object is "equal to" this JsonArray. An object is considered equal
|
|
||||||
* if it is also a <code>JsonArray</code> and both arrays contain the same list of values.
|
|
||||||
* <p>
|
|
||||||
* If two JsonArrays are equal, they will also produce the same JSON output.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param object the object to be compared with this JsonArray
|
|
||||||
* @return {@code true} if the specified object is equal to this JsonArray, <code>false</code>
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
if (this == object) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (object == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != object.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonArray other = (JsonArray) object;
|
|
||||||
return values.equals(other.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
261
src/main/java/org/xbib/marc/json/JsonBuilder.java
Normal file
261
src/main/java/org/xbib/marc/json/JsonBuilder.java
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed 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
|
||||||
|
*
|
||||||
|
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
||||||
|
*
|
||||||
|
* 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.xbib.marc.json;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class JsonBuilder {
|
||||||
|
|
||||||
|
private final Appendable appendable;
|
||||||
|
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
protected JsonBuilder() {
|
||||||
|
this(new StringBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JsonBuilder(Appendable appendable) {
|
||||||
|
this.appendable = appendable;
|
||||||
|
this.state = new State(null, 0, Structure.DOCSTART, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder beginCollection() throws IOException {
|
||||||
|
this.state = new State(state, state.level + 1, Structure.COLLECTION, true);
|
||||||
|
appendable.append('[');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder endCollection() throws IOException {
|
||||||
|
if (state.structure != Structure.COLLECTION) {
|
||||||
|
throw new IOException("no array to close");
|
||||||
|
}
|
||||||
|
appendable.append(']');
|
||||||
|
this.state = state != null ? state.parent : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder beginMap() throws IOException {
|
||||||
|
if (state.structure == Structure.COLLECTION) {
|
||||||
|
beginArrayValue();
|
||||||
|
}
|
||||||
|
this.state = new State(state, state.level + 1, Structure.MAP, true);
|
||||||
|
appendable.append('{');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder endMap() throws IOException {
|
||||||
|
if (state.structure != Structure.MAP && state.structure != Structure.KEY) {
|
||||||
|
throw new IOException("no object to close");
|
||||||
|
}
|
||||||
|
appendable.append('}');
|
||||||
|
this.state = state != null ? state.parent : null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder buildMap(Map<String, Object> map) throws IOException {
|
||||||
|
Objects.requireNonNull(map);
|
||||||
|
boolean wrap = state.structure != Structure.MAP;
|
||||||
|
if (wrap) {
|
||||||
|
beginMap();
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||||
|
buildKey(entry.getKey());
|
||||||
|
buildValue(entry.getValue());
|
||||||
|
}
|
||||||
|
if (wrap) {
|
||||||
|
endMap();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder buildCollection(Collection<?> collection) throws IOException {
|
||||||
|
Objects.requireNonNull(collection);
|
||||||
|
beginCollection();
|
||||||
|
for (Object object : collection) {
|
||||||
|
buildValue(object);
|
||||||
|
}
|
||||||
|
endCollection();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public JsonBuilder buildValue(Object object) throws IOException {
|
||||||
|
if (object instanceof Map) {
|
||||||
|
buildMap((Map<String, Object>) object);
|
||||||
|
return this;
|
||||||
|
} else if (object instanceof Collection) {
|
||||||
|
buildCollection((Collection<Object>) object);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (state.structure == Structure.COLLECTION) {
|
||||||
|
beginArrayValue();
|
||||||
|
}
|
||||||
|
if (object == null) {
|
||||||
|
buildNull();
|
||||||
|
} else if (object instanceof CharSequence) {
|
||||||
|
buildString((CharSequence) object, true);
|
||||||
|
} else if (object instanceof Boolean) {
|
||||||
|
buildBoolean((Boolean) object);
|
||||||
|
} else if (object instanceof Byte) {
|
||||||
|
buildNumber((byte) object);
|
||||||
|
} else if (object instanceof Integer) {
|
||||||
|
buildNumber((int) object);
|
||||||
|
} else if (object instanceof Long) {
|
||||||
|
buildNumber((long) object);
|
||||||
|
} else if (object instanceof Float) {
|
||||||
|
buildNumber((float) object);
|
||||||
|
} else if (object instanceof Double) {
|
||||||
|
buildNumber((double) object);
|
||||||
|
} else if (object instanceof Number) {
|
||||||
|
buildNumber((Number) object);
|
||||||
|
} else if (object instanceof Instant) {
|
||||||
|
buildInstant((Instant) object);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("unable to write object class " + object.getClass());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder buildKey(CharSequence string) throws IOException {
|
||||||
|
if (state.structure == Structure.COLLECTION) {
|
||||||
|
beginArrayValue();
|
||||||
|
} else if (state.structure == Structure.MAP || state.structure == Structure.KEY) {
|
||||||
|
beginKey(string != null ? string.toString() : null);
|
||||||
|
}
|
||||||
|
buildString(string, true);
|
||||||
|
if (state.structure == Structure.MAP || state.structure == Structure.KEY) {
|
||||||
|
endKey(string != null ? string.toString() : null);
|
||||||
|
}
|
||||||
|
state.structure = Structure.KEY;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonBuilder buildNull() throws IOException {
|
||||||
|
buildString("null", false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String build() {
|
||||||
|
return appendable.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginKey(String k) throws IOException {
|
||||||
|
if (state.first) {
|
||||||
|
state.first = false;
|
||||||
|
} else {
|
||||||
|
appendable.append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endKey(String k) throws IOException {
|
||||||
|
appendable.append(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginArrayValue() throws IOException {
|
||||||
|
if (state.first) {
|
||||||
|
state.first = false;
|
||||||
|
} else {
|
||||||
|
appendable.append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildBoolean(boolean bool) throws IOException {
|
||||||
|
buildString(bool ? "true" : "false", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildNumber(Number number) throws IOException {
|
||||||
|
buildString(number != null ? number.toString() : null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildInstant(Instant instant) throws IOException {
|
||||||
|
buildString(instant.toString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildString(CharSequence string, boolean escape) throws IOException {
|
||||||
|
appendable.append(escape ? escapeString(string) : string);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharSequence escapeString(CharSequence string) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append('"');
|
||||||
|
int start = 0;
|
||||||
|
int l = string.length();
|
||||||
|
for (int i = 0; i < l; i++) {
|
||||||
|
char c = string.charAt(i);
|
||||||
|
// In JavaScript, U+2028 and U+2029 characters count as line endings and must be encoded.
|
||||||
|
// http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character
|
||||||
|
if (c == '"' || c == '\\' || c < 32 || c == '\u2028' || c == '\u2029') {
|
||||||
|
if (i > start) {
|
||||||
|
sb.append(string, start, i);
|
||||||
|
}
|
||||||
|
start = i + 1;
|
||||||
|
sb.append(escapeCharacter(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (l > start) {
|
||||||
|
sb.append(string, start, l);
|
||||||
|
}
|
||||||
|
sb.append('"');
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String escapeCharacter(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case '\n' -> {
|
||||||
|
return "\\n";
|
||||||
|
}
|
||||||
|
case '\r' -> {
|
||||||
|
return "\\r";
|
||||||
|
}
|
||||||
|
case '\t' -> {
|
||||||
|
return "\\t";
|
||||||
|
}
|
||||||
|
case '\\' -> {
|
||||||
|
return "\\\\";
|
||||||
|
}
|
||||||
|
case '\'' -> {
|
||||||
|
return "\\'";
|
||||||
|
}
|
||||||
|
case '\"' -> {
|
||||||
|
return "\\\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String hex = Integer.toHexString(c);
|
||||||
|
return "\\u0000".substring(0, 6 - hex.length()) + hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Structure {
|
||||||
|
DOCSTART, MAP, KEY, COLLECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class State {
|
||||||
|
State parent;
|
||||||
|
int level;
|
||||||
|
Structure structure;
|
||||||
|
boolean first;
|
||||||
|
|
||||||
|
State(State parent, int level, Structure structure, boolean first) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.level = level;
|
||||||
|
this.structure = structure;
|
||||||
|
this.first = first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,110 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
public class JsonDefaultHandler implements JsonHandler<JsonArray, JsonObject> {
|
|
||||||
|
|
||||||
protected JsonValue value;
|
|
||||||
|
|
||||||
public JsonDefaultHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonValue getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonArray startArray() {
|
|
||||||
return new JsonArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonObject startObject() {
|
|
||||||
return new JsonObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startNull() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endNull() {
|
|
||||||
value = JsonLiteral.NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startBoolean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endBoolean(boolean bool) {
|
|
||||||
value = bool ? JsonLiteral.TRUE : JsonLiteral.FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startString() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endString(String string) {
|
|
||||||
value = new JsonString(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startNumber() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endNumber(String string) {
|
|
||||||
value = new JsonNumber(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endArray(JsonArray array) {
|
|
||||||
value = array;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startArrayValue(JsonArray array) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObject(JsonObject object) {
|
|
||||||
value = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startObjectName(JsonObject object) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObjectName(JsonObject object, String name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startObjectValue(JsonObject object, String name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endArrayValue(JsonArray array) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObjectValue(JsonObject object, String name) {
|
|
||||||
object.add(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
public class JsonException extends RuntimeException {
|
|
||||||
|
|
||||||
JsonException(Throwable throwable) {
|
|
||||||
super(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for parser events. A {@link JsonHandler} can be given to a {@link JsonReader}. The
|
|
||||||
* parser will then call the methods of the given handler while reading the input.
|
|
||||||
|
|
||||||
* <p>
|
|
||||||
* Implementations that build an object representation of the parsed JSON can return arbitrary handler
|
|
||||||
* objects for JSON arrays and JSON objects in {@link #startArray()} and {@link #startObject()}.
|
|
||||||
* These handler objects will then be provided in all subsequent parser events for this particular
|
|
||||||
* array or object. They can be used to keep track the elements of a JSON array or object.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param <A> The type of handlers used for JSON arrays
|
|
||||||
* @param <O> The type of handlers used for JSON objects
|
|
||||||
* @see JsonReader
|
|
||||||
*/
|
|
||||||
public interface JsonHandler<A, O> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of a <code>null</code> literal in the JSON input. This method will be
|
|
||||||
* called when reading the first character of the literal.
|
|
||||||
*/
|
|
||||||
void startNull();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of a <code>null</code> literal in the JSON input. This method will be called
|
|
||||||
* after reading the last character of the literal.
|
|
||||||
*/
|
|
||||||
void endNull();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of a boolean literal (<code>true</code> or <code>false</code>) in the
|
|
||||||
* JSON input. This method will be called when reading the first character of the literal.
|
|
||||||
*/
|
|
||||||
void startBoolean();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of a boolean literal (<code>true</code> or <code>false</code>) in the JSON
|
|
||||||
* input. This method will be called after reading the last character of the literal.
|
|
||||||
*
|
|
||||||
* @param value the parsed boolean value
|
|
||||||
*/
|
|
||||||
void endBoolean(boolean value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of a string in the JSON input. This method will be called when reading
|
|
||||||
* the opening double quote character (<code>'"'</code>).
|
|
||||||
*/
|
|
||||||
void startString();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of a string in the JSON input. This method will be called after reading the
|
|
||||||
* closing double quote character (<code>'"'</code>).
|
|
||||||
*
|
|
||||||
* @param string the parsed string
|
|
||||||
*/
|
|
||||||
void endString(String string);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of a number in the JSON input. This method will be called when reading
|
|
||||||
* the first character of the number.
|
|
||||||
*/
|
|
||||||
void startNumber();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of a number in the JSON input. This method will be called after reading the
|
|
||||||
* last character of the number.
|
|
||||||
*
|
|
||||||
* @param string the parsed number string
|
|
||||||
*/
|
|
||||||
void endNumber(String string);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of an array in the JSON input. This method will be called when reading
|
|
||||||
* the opening square bracket character (<code>'['</code>).
|
|
||||||
* <p>
|
|
||||||
* This method may return an object to handle subsequent parser events for this array. This array
|
|
||||||
* handler will then be provided in all calls to {@link #startArrayValue(Object)
|
|
||||||
* startArrayValue()}, {@link #endArrayValue(Object) endArrayValue()}, and
|
|
||||||
* {@link #endArray(Object) endArray()} for this array.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return a handler for this array, or <code>null</code> if not needed
|
|
||||||
*/
|
|
||||||
A startArray();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of an array in the JSON input. This method will be called after reading the
|
|
||||||
* closing square bracket character (<code>']'</code>).
|
|
||||||
*
|
|
||||||
* @param array the array handler returned from {@link #startArray()}, or <code>null</code> if not
|
|
||||||
* provided
|
|
||||||
*/
|
|
||||||
void endArray(A array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of an array element in the JSON input. This method will be called when
|
|
||||||
* reading the first character of the element, just before the call to the <code>start</code>
|
|
||||||
* method for the specific element type ({@link #startString()}, {@link #startNumber()}, etc.).
|
|
||||||
*
|
|
||||||
* @param array the array handler returned from {@link #startArray()}, or <code>null</code> if not
|
|
||||||
* provided
|
|
||||||
*/
|
|
||||||
void startArrayValue(A array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of an array element in the JSON input. This method will be called after
|
|
||||||
* reading the last character of the element value, just after the <code>end</code> method for the
|
|
||||||
* specific element type (like {@link #endString(String) endString()}, {@link #endNumber(String)
|
|
||||||
* endNumber()}, etc.).
|
|
||||||
*
|
|
||||||
* @param array the array handler returned from {@link #startArray()}, or <code>null</code> if not
|
|
||||||
* provided
|
|
||||||
*/
|
|
||||||
void endArrayValue(A array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of an object in the JSON input. This method will be called when reading
|
|
||||||
* the opening curly bracket character (<code>'{'</code>).
|
|
||||||
* <p>
|
|
||||||
* This method may return an object to handle subsequent parser events for this object. This
|
|
||||||
* object handler will be provided in all calls to {@link #startObjectName(Object)
|
|
||||||
* startObjectName()}, {@link #endObjectName(Object, String) endObjectName()},
|
|
||||||
* {@link #startObjectValue(Object, String) startObjectValue()},
|
|
||||||
* {@link #endObjectValue(Object, String) endObjectValue()}, and {@link #endObject(Object)
|
|
||||||
* endObject()} for this object.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return a handler for this object, or <code>null</code> if not needed
|
|
||||||
*/
|
|
||||||
O startObject();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of an object in the JSON input. This method will be called after reading the
|
|
||||||
* closing curly bracket character (<code>'}'</code>).
|
|
||||||
*
|
|
||||||
* @param object the object handler returned from {@link #startObject()}, or null if not provided
|
|
||||||
*/
|
|
||||||
void endObject(O object);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of the name of an object member in the JSON input. This method will be
|
|
||||||
* called when reading the opening quote character ('"') of the member name.
|
|
||||||
*
|
|
||||||
* @param object the object handler returned from {@link #startObject()}, or <code>null</code> if not
|
|
||||||
* provided
|
|
||||||
*/
|
|
||||||
void startObjectName(O object);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of an object member name in the JSON input. This method will be called after
|
|
||||||
* reading the closing quote character (<code>'"'</code>) of the member name.
|
|
||||||
*
|
|
||||||
* @param object the object handler returned from {@link #startObject()}, or null if not provided
|
|
||||||
* @param name the parsed member name
|
|
||||||
*/
|
|
||||||
void endObjectName(O object, String name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the beginning of the name of an object member in the JSON input. This method will be
|
|
||||||
* called when reading the opening quote character ('"') of the member name.
|
|
||||||
*
|
|
||||||
* @param object the object handler returned from {@link #startObject()}, or <code>null</code> if not
|
|
||||||
* provided
|
|
||||||
* @param name the member name
|
|
||||||
*/
|
|
||||||
void startObjectValue(O object, String name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates the end of an object member value in the JSON input. This method will be called after
|
|
||||||
* reading the last character of the member value, just after the <code>end</code> method for the
|
|
||||||
* specific member type (like {@link #endString(String) endString()}, {@link #endNumber(String)
|
|
||||||
* endNumber()}, etc.).
|
|
||||||
*
|
|
||||||
* @param object the object handler returned from {@link #startObject()}, or null if not provided
|
|
||||||
* @param name the parsed member name
|
|
||||||
*/
|
|
||||||
void endObjectValue(O object, String name);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A JSON literal.
|
|
||||||
*/
|
|
||||||
class JsonLiteral extends JsonValue {
|
|
||||||
|
|
||||||
public static final JsonLiteral NULL = new JsonLiteral("null");
|
|
||||||
|
|
||||||
public static final JsonLiteral TRUE = new JsonLiteral("true");
|
|
||||||
|
|
||||||
public static final JsonLiteral FALSE = new JsonLiteral("false");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private final boolean isNull;
|
|
||||||
|
|
||||||
private final boolean isTrue;
|
|
||||||
|
|
||||||
private final boolean isFalse;
|
|
||||||
|
|
||||||
JsonLiteral(String value) {
|
|
||||||
this.value = value;
|
|
||||||
isNull = "null".equals(value);
|
|
||||||
isTrue = "true".equals(value);
|
|
||||||
isFalse = "false".equals(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(JsonWriter writer) throws IOException {
|
|
||||||
writer.writeLiteral(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return value.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isNull() {
|
|
||||||
return isNull;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTrue() {
|
|
||||||
return isTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFalse() {
|
|
||||||
return isFalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBoolean() {
|
|
||||||
return isTrue || isFalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean asBoolean() {
|
|
||||||
return isNull ? super.asBoolean() : isTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
return this == object || object != null && getClass() == object.getClass() && value.equals(((JsonLiteral) object).value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class JsonMapper {
|
|
||||||
|
|
||||||
private JsonMapper() {
|
|
||||||
// do not instantiate this class
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object asObject(JsonValue value) {
|
|
||||||
if (value.isBoolean()) {
|
|
||||||
return value.asBoolean();
|
|
||||||
} else if (value.isInt()) {
|
|
||||||
return value.asInt();
|
|
||||||
} else if (value.isLong()) {
|
|
||||||
return value.asLong();
|
|
||||||
} else if (value.isFloat()) {
|
|
||||||
return value.asFloat();
|
|
||||||
} else if (value.isDouble()) {
|
|
||||||
return value.asDouble();
|
|
||||||
} else if (value.isString()) {
|
|
||||||
return value.asString();
|
|
||||||
} else if (value.isArray()) {
|
|
||||||
return asList(value.asArray());
|
|
||||||
} else if (value.isObject()) {
|
|
||||||
return asMap(value.asObject());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Object> asList(JsonArray array) {
|
|
||||||
List<Object> list = new ArrayList<>(array.size());
|
|
||||||
for (JsonValue element : array) {
|
|
||||||
list.add(asObject(element));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, Object> asMap(JsonObject object) {
|
|
||||||
Map<String, Object> map = new HashMap<>(object.size(), 1.f);
|
|
||||||
for (JsonObject.Member member : object) {
|
|
||||||
map.put(member.getName(), asObject(member.getValue()));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonValue asJsonValue(Object object) {
|
|
||||||
if (object == null) {
|
|
||||||
return JsonLiteral.NULL;
|
|
||||||
} else if (object instanceof Boolean) {
|
|
||||||
return Json.of((Boolean) object);
|
|
||||||
} else if (object instanceof Integer) {
|
|
||||||
return Json.of((Integer) object);
|
|
||||||
} else if (object instanceof Long) {
|
|
||||||
return Json.of((Long) object);
|
|
||||||
} else if (object instanceof Float) {
|
|
||||||
return Json.of((Float) object);
|
|
||||||
} else if (object instanceof Double) {
|
|
||||||
return Json.of((Double) object);
|
|
||||||
} else if (object instanceof String) {
|
|
||||||
return Json.of((String) object);
|
|
||||||
} else if (object instanceof Collection) {
|
|
||||||
return asJsonArray((Collection<?>) object);
|
|
||||||
} else if (object instanceof Map) {
|
|
||||||
return asJsonObject((Map<?, ?>) object);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonArray asJsonArray(Collection<?> collection) {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (Object element : collection) {
|
|
||||||
array.add(asJsonValue(element));
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonObject asJsonObject(Map<?, ?> map) {
|
|
||||||
JsonObject object = new JsonObject();
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
||||||
object.add(String.valueOf(entry.getKey()),
|
|
||||||
asJsonValue(entry.getValue()));
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class JsonNumber extends JsonValue {
|
|
||||||
|
|
||||||
private final String string;
|
|
||||||
|
|
||||||
JsonNumber(String string) {
|
|
||||||
Objects.requireNonNull(string);
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(JsonWriter writer) throws IOException {
|
|
||||||
writer.writeNumber(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInt() {
|
|
||||||
try {
|
|
||||||
asInt();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLong() {
|
|
||||||
try {
|
|
||||||
asLong();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFloat() {
|
|
||||||
try {
|
|
||||||
asFloat();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDouble() {
|
|
||||||
try {
|
|
||||||
asDouble();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int asInt() {
|
|
||||||
return Integer.parseInt(string, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long asLong() {
|
|
||||||
return Long.parseLong(string, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float asFloat() {
|
|
||||||
return Float.parseFloat(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double asDouble() {
|
|
||||||
return Double.parseDouble(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return string.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
return this == object || object != null && getClass() == object.getClass()
|
|
||||||
&& string.equals(((JsonNumber) object).string);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,785 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a JSON object, a set of name/value pairs, where the names are strings and the values
|
|
||||||
* are JSON values.
|
|
||||||
* <p>
|
|
||||||
* Members can be added using the <code>add(String, ...)</code> methods which accept instances of
|
|
||||||
* {@link JsonValue}, strings, primitive numbers, and boolean values. To modify certain values of an
|
|
||||||
* object, use the <code>set(String, ...)</code> methods. Please note that the <code>add</code>
|
|
||||||
* methods are faster than <code>set</code> as they do not search for existing members. On the other
|
|
||||||
* hand, the <code>add</code> methods do not prevent adding multiple members with the same name.
|
|
||||||
* Duplicate names are discouraged but not prohibited by JSON.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Members can be accessed by their name using {@link #get(String)}. A list of all names can be
|
|
||||||
* obtained from the method {@link #names()}. This class also supports iterating over the members in
|
|
||||||
* document order using an {@link #iterator()} or an enhanced for loop:
|
|
||||||
* </p>
|
|
||||||
* <pre>
|
|
||||||
* for (Member member : jsonObject) {
|
|
||||||
* String name = member.getName();
|
|
||||||
* JsonValue value = member.getValue();
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
* <p>
|
|
||||||
* Even though JSON objects are unordered by definition, instances of this class preserve the order
|
|
||||||
* of members to allow processing in document order and to guarantee a predictable output.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Note that this class is <strong>not thread-safe</strong>. If multiple threads access a
|
|
||||||
* <code>JsonObject</code> instance concurrently, while at least one of these threads modifies the
|
|
||||||
* contents of this object, access to the instance must be synchronized externally. Failure to do so
|
|
||||||
* may lead to an inconsistent state.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class JsonObject extends JsonValue implements Iterable<JsonObject.Member> {
|
|
||||||
|
|
||||||
private final List<String> names;
|
|
||||||
|
|
||||||
private final List<JsonValue> values;
|
|
||||||
|
|
||||||
private HashIndexTable table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new empty JsonObject.
|
|
||||||
*/
|
|
||||||
public JsonObject() {
|
|
||||||
names = new ArrayList<>();
|
|
||||||
values = new ArrayList<>();
|
|
||||||
table = new HashIndexTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonObject, initialized with the contents of the specified JSON object.
|
|
||||||
*
|
|
||||||
* @param object the JSON object to get the initial contents from, must not be <code>null</code>
|
|
||||||
*/
|
|
||||||
public JsonObject(JsonObject object) {
|
|
||||||
Objects.requireNonNull(object);
|
|
||||||
names = new ArrayList<>(object.names);
|
|
||||||
values = new ArrayList<>(object.values);
|
|
||||||
table = new HashIndexTable();
|
|
||||||
updateHashIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified <code>int</code> value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, int value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified <code>long</code> value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, long value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified <code>float</code> value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, float value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified <code>double</code> value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, double value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified <code>boolean</code> value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, boolean value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the JSON
|
|
||||||
* representation of the specified string.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, String value) {
|
|
||||||
add(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a new member to the end of this object, with the specified name and the specified JSON
|
|
||||||
* value.
|
|
||||||
* <p>
|
|
||||||
* This method <strong>does not prevent duplicate names</strong>. Calling this method with a name
|
|
||||||
* that already exists in the object will append another member with the same name. In order to
|
|
||||||
* replace existing members, use the method <code>set(name, value)</code> instead. However,
|
|
||||||
* <strong> <em>add</em> is much faster than <em>set</em></strong> (because it does not need to
|
|
||||||
* search for existing members). Therefore <em>add</em> should be preferred when constructing new
|
|
||||||
* objects.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add, must not be <code>null</code>
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject add(String name, JsonValue value) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new NullPointerException("name is null");
|
|
||||||
}
|
|
||||||
if (value == null) {
|
|
||||||
throw new NullPointerException("value is null");
|
|
||||||
}
|
|
||||||
table.add(name, names.size());
|
|
||||||
names.add(name);
|
|
||||||
values.add(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified <code>int</code> value. If this object does not contain a member with this name, a
|
|
||||||
* new member is added at the end of the object. If this object contains multiple members with
|
|
||||||
* this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to replace
|
|
||||||
* @param value the value to set to the member
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, int value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified <code>long</code> value. If this object does not contain a member with this name, a
|
|
||||||
* new member is added at the end of the object. If this object contains multiple members with
|
|
||||||
* this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to replace
|
|
||||||
* @param value the value to set to the member
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, long value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified <code>float</code> value. If this object does not contain a member with this name, a
|
|
||||||
* new member is added at the end of the object. If this object contains multiple members with
|
|
||||||
* this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, float value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified <code>double</code> value. If this object does not contain a member with this name, a
|
|
||||||
* new member is added at the end of the object. If this object contains multiple members with
|
|
||||||
* this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, double value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified <code>boolean</code> value. If this object does not contain a member with this name,
|
|
||||||
* a new member is added at the end of the object. If this object contains multiple members with
|
|
||||||
* this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, boolean value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the JSON representation of the
|
|
||||||
* specified string. If this object does not contain a member with this name, a new member is
|
|
||||||
* added at the end of the object. If this object contains multiple members with this name, only
|
|
||||||
* the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, String value) {
|
|
||||||
set(name, Json.of(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of the member with the specified name to the specified JSON value. If this
|
|
||||||
* object does not contain a member with this name, a new member is added at the end of the
|
|
||||||
* object. If this object contains multiple members with this name, only the last one is changed.
|
|
||||||
* <p>
|
|
||||||
* This method should <strong>only be used to modify existing objects</strong>. To fill a new
|
|
||||||
* object with members, the method <code>add(name, value)</code> should be preferred which is much
|
|
||||||
* faster (as it does not need to search for existing members).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param name the name of the member to add
|
|
||||||
* @param value the value of the member to add, must not be <code>null</code>
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject set(String name, JsonValue value) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new NullPointerException("name is null");
|
|
||||||
}
|
|
||||||
if (value == null) {
|
|
||||||
throw new NullPointerException("value is null");
|
|
||||||
}
|
|
||||||
int index = indexOf(name);
|
|
||||||
if (index != -1) {
|
|
||||||
values.set(index, value);
|
|
||||||
} else {
|
|
||||||
table.add(name, names.size());
|
|
||||||
names.add(name);
|
|
||||||
values.add(value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a member with the specified name from this object. If this object contains multiple
|
|
||||||
* members with the given name, only the last one is removed. If this object does not contain a
|
|
||||||
* member with the specified name, the object is not modified.
|
|
||||||
*
|
|
||||||
* @param name the name of the member to remove
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject remove(String name) {
|
|
||||||
Objects.requireNonNull(name);
|
|
||||||
int index = indexOf(name);
|
|
||||||
if (index != -1) {
|
|
||||||
table.remove(index);
|
|
||||||
names.remove(index);
|
|
||||||
values.remove(index);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies all members of the specified object into this object. When the specified object contains
|
|
||||||
* members with names that also exist in this object, the existing values in this object will be
|
|
||||||
* replaced by the corresponding values in the specified object.
|
|
||||||
*
|
|
||||||
* @param object the object to merge
|
|
||||||
* @return the object itself, to enable method chaining
|
|
||||||
*/
|
|
||||||
public JsonObject merge(JsonObject object) {
|
|
||||||
Objects.requireNonNull(object);
|
|
||||||
for (Member member : object) {
|
|
||||||
this.set(member.name, member.value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of the member with the specified name in this object. If this object contains
|
|
||||||
* multiple members with the given name, this method will return the last one.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @return the value of the last member with the specified name, or <code>null</code> if this
|
|
||||||
* object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public JsonValue get(String name) {
|
|
||||||
Objects.requireNonNull(name);
|
|
||||||
int index = indexOf(name);
|
|
||||||
return index != -1 ? values.get(index) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>int</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one will be picked. If this
|
|
||||||
* member's value does not represent a JSON number or if it cannot be interpreted as Java
|
|
||||||
* <code>int</code>, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public int getInt(String name, int defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asInt() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>long</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one will be picked. If this
|
|
||||||
* member's value does not represent a JSON number or if it cannot be interpreted as Java
|
|
||||||
* <code>long</code>, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public long getLong(String name, long defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asLong() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>float</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one will be picked. If this
|
|
||||||
* member's value does not represent a JSON number or if it cannot be interpreted as Java
|
|
||||||
* <code>float</code>, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public float getFloat(String name, float defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asFloat() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>double</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one will be picked. If this
|
|
||||||
* member's value does not represent a JSON number or if it cannot be interpreted as Java
|
|
||||||
* <code>double</code>, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public double getDouble(String name, double defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asDouble() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>boolean</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one will be picked. If this
|
|
||||||
* member's value does not represent a JSON <code>true</code> or <code>false</code> value, an
|
|
||||||
* exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public boolean getBoolean(String name, boolean defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asBoolean() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the <code>String</code> value of the member with the specified name in this object. If
|
|
||||||
* this object does not contain a member with this name, the given default value is returned. If
|
|
||||||
* this object contains multiple members with the given name, the last one is picked. If this
|
|
||||||
* member's value does not represent a JSON string, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @param name the name of the member whose value is to be returned
|
|
||||||
* @param defaultValue the value to be returned if the requested member is missing
|
|
||||||
* @return the value of the last member with the specified name, or the given default value if
|
|
||||||
* this object does not contain a member with that name
|
|
||||||
*/
|
|
||||||
public String getString(String name, String defaultValue) {
|
|
||||||
JsonValue value = get(name);
|
|
||||||
return value != null ? value.asString() : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of members (name/value pairs) in this object.
|
|
||||||
*
|
|
||||||
* @return the number of members in this object
|
|
||||||
*/
|
|
||||||
public int size() {
|
|
||||||
return names.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns <code>true</code> if this object contains no members.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this object contains no members
|
|
||||||
*/
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return names.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of the names in this object in document order. The returned list is backed by
|
|
||||||
* this object and will reflect subsequent changes. It cannot be used to modify this object.
|
|
||||||
* Attempts to modify the returned list will result in an exception.
|
|
||||||
*
|
|
||||||
* @return a list of the names in this object
|
|
||||||
*/
|
|
||||||
public List<String> names() {
|
|
||||||
return Collections.unmodifiableList(names);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an iterator over the members of this object in document order. The returned iterator
|
|
||||||
* cannot be used to modify this object.
|
|
||||||
*
|
|
||||||
* @return an iterator over the members of this object
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<Member> iterator() {
|
|
||||||
final Iterator<String> namesIterator = names.iterator();
|
|
||||||
final Iterator<JsonValue> valuesIterator = values.iterator();
|
|
||||||
return new Iterator<Member>() {
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return namesIterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Member next() {
|
|
||||||
String name = namesIterator.next();
|
|
||||||
JsonValue value = valuesIterator.next();
|
|
||||||
return new Member(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(JsonWriter writer) throws IOException {
|
|
||||||
writer.writeObjectOpen();
|
|
||||||
Iterator<String> namesIterator = names.iterator();
|
|
||||||
Iterator<JsonValue> valuesIterator = values.iterator();
|
|
||||||
if (namesIterator.hasNext()) {
|
|
||||||
writer.writeMemberName(namesIterator.next());
|
|
||||||
writer.writeMemberSeparator();
|
|
||||||
valuesIterator.next().write(writer);
|
|
||||||
while (namesIterator.hasNext()) {
|
|
||||||
writer.writeObjectSeparator();
|
|
||||||
writer.writeMemberName(namesIterator.next());
|
|
||||||
writer.writeMemberSeparator();
|
|
||||||
valuesIterator.next().write(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.writeObjectClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isObject() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonObject asObject() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = 1;
|
|
||||||
result = 31 * result + names.hashCode();
|
|
||||||
result = 31 * result + values.hashCode();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != obj.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonObject other = (JsonObject) obj;
|
|
||||||
return names.equals(other.names) && values.equals(other.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
int indexOf(String name) {
|
|
||||||
int index = table.get(name);
|
|
||||||
if (index != -1 && name.equals(names.get(index))) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
return names.lastIndexOf(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateHashIndex() {
|
|
||||||
int size = names.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
table.add(names.get(i), i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a member of a JSON object, a pair of a name and a value.
|
|
||||||
*/
|
|
||||||
public static class Member {
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final JsonValue value;
|
|
||||||
|
|
||||||
public Member(String name, JsonValue value) {
|
|
||||||
this.name = name;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of this member.
|
|
||||||
*
|
|
||||||
* @return the name of this member, never <code>null</code>
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of this member.
|
|
||||||
*
|
|
||||||
* @return the value of this member, never <code>null</code>
|
|
||||||
*/
|
|
||||||
public JsonValue getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = 1;
|
|
||||||
result = 31 * result + name.hashCode();
|
|
||||||
result = 31 * result + value.hashCode();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether a given object is "equal to" this JsonObject. An object is considered equal
|
|
||||||
* if it is also a <code>JsonObject</code> and both objects contain the same members <em>in
|
|
||||||
* the same order</em>.
|
|
||||||
* <p>
|
|
||||||
* If two JsonObjects are equal, they will also produce the same JSON output.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param object the object to be compared with this JsonObject
|
|
||||||
* @return <tt>true</tt> if the specified object is equal to this JsonObject, <code>false</code>
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
if (this == object) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (object == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != object.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Member other = (Member) object;
|
|
||||||
return name.equals(other.name) && value.equals(other.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static class HashIndexTable {
|
|
||||||
|
|
||||||
private final byte[] hashTable = new byte[32]; // must be a power of two
|
|
||||||
|
|
||||||
HashIndexTable() {
|
|
||||||
}
|
|
||||||
|
|
||||||
HashIndexTable(HashIndexTable original) {
|
|
||||||
System.arraycopy(original.hashTable, 0, hashTable, 0, hashTable.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(String name, int index) {
|
|
||||||
int slot = hashSlotFor(name);
|
|
||||||
if (index < 0xff) {
|
|
||||||
// increment by 1, 0 stands for empty
|
|
||||||
hashTable[slot] = (byte) (index + 1);
|
|
||||||
} else {
|
|
||||||
hashTable[slot] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(int index) {
|
|
||||||
for (int i = 0; i < hashTable.length; i++) {
|
|
||||||
if (hashTable[i] == index + 1) {
|
|
||||||
hashTable[i] = 0;
|
|
||||||
} else if (hashTable[i] > index + 1) {
|
|
||||||
hashTable[i]--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int get(Object name) {
|
|
||||||
int slot = hashSlotFor(name);
|
|
||||||
// subtract 1, 0 stands for empty
|
|
||||||
return (hashTable[slot] & 0xff) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int hashSlotFor(Object element) {
|
|
||||||
return element.hashCode() & hashTable.length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,460 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A streaming parser for JSON text. The parser reports all events to a given handler.
|
|
||||||
*
|
|
||||||
* @param <A> the JSON array type
|
|
||||||
* @param <O> the JSON object type
|
|
||||||
*/
|
|
||||||
public class JsonReader<A, O> {
|
|
||||||
|
|
||||||
private static final int MAX_NESTING_LEVEL = 1000;
|
|
||||||
|
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 1024;
|
|
||||||
|
|
||||||
private final Reader reader;
|
|
||||||
|
|
||||||
private final JsonHandler<A, O> handler;
|
|
||||||
|
|
||||||
private char[] buffer;
|
|
||||||
|
|
||||||
private int index;
|
|
||||||
|
|
||||||
private int fill;
|
|
||||||
|
|
||||||
private int current;
|
|
||||||
|
|
||||||
private StringBuilder captureBuffer;
|
|
||||||
|
|
||||||
private int captureStart;
|
|
||||||
|
|
||||||
private int nestingLevel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JsonParser with the given handler. The parser will report all parser events to
|
|
||||||
* this handler.
|
|
||||||
* @param reader the reader
|
|
||||||
* @param handler the handler to process parser events
|
|
||||||
*/
|
|
||||||
public JsonReader(Reader reader, JsonHandler<A, O> handler) {
|
|
||||||
Objects.requireNonNull(handler);
|
|
||||||
this.handler = handler;
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entire input from the given reader and parses it as JSON. The input must contain a
|
|
||||||
* valid JSON value, optionally padded with whitespace.
|
|
||||||
* <p>
|
|
||||||
* Characters are read in chunks into a default-sized input buffer. Hence, wrapping a reader in an
|
|
||||||
* additional <code>BufferedReader</code> likely won't improve reading performance.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @throws IOException if an I/O error occurs in the reader
|
|
||||||
* @throws JsonException if the input is not valid JSON
|
|
||||||
*/
|
|
||||||
public void parse() throws IOException {
|
|
||||||
parse(DEFAULT_BUFFER_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entire input from the given reader and parses it as JSON. The input must contain a
|
|
||||||
* valid JSON value, optionally padded with whitespace.
|
|
||||||
* <p>
|
|
||||||
* Characters are read in chunks into an input buffer of the given size. Hence, wrapping a reader
|
|
||||||
* in an additional <code>BufferedReader</code> likely won't improve reading performance.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param buffersize the size of the input buffer in chars
|
|
||||||
* @throws IOException if an I/O error occurs in the reader
|
|
||||||
* @throws JsonException if the input is not valid JSON
|
|
||||||
*/
|
|
||||||
public void parse(int buffersize) throws IOException {
|
|
||||||
if (reader == null) {
|
|
||||||
throw new NullPointerException("reader is null");
|
|
||||||
}
|
|
||||||
if (buffersize <= 0) {
|
|
||||||
throw new IllegalArgumentException("buffersize is zero or negative");
|
|
||||||
}
|
|
||||||
buffer = new char[buffersize];
|
|
||||||
index = 0;
|
|
||||||
fill = 0;
|
|
||||||
current = 0;
|
|
||||||
captureStart = -1;
|
|
||||||
read();
|
|
||||||
skipWhiteSpace();
|
|
||||||
readValue();
|
|
||||||
skipWhiteSpace();
|
|
||||||
if (!isEndOfText()) {
|
|
||||||
throw error("Unexpected character");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readValue() throws IOException {
|
|
||||||
switch (current) {
|
|
||||||
case 'n':
|
|
||||||
readNull();
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
readTrue();
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
readFalse();
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
readString();
|
|
||||||
break;
|
|
||||||
case '[':
|
|
||||||
readArray();
|
|
||||||
break;
|
|
||||||
case '{':
|
|
||||||
readObject();
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
readNumber();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw expected("value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readArray() throws IOException {
|
|
||||||
A array = handler.startArray();
|
|
||||||
read();
|
|
||||||
if (++nestingLevel > MAX_NESTING_LEVEL) {
|
|
||||||
throw error("Nesting too deep");
|
|
||||||
}
|
|
||||||
skipWhiteSpace();
|
|
||||||
if (readChar(']')) {
|
|
||||||
nestingLevel--;
|
|
||||||
handler.endArray(array);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
skipWhiteSpace();
|
|
||||||
handler.startArrayValue(array);
|
|
||||||
readValue();
|
|
||||||
handler.endArrayValue(array);
|
|
||||||
skipWhiteSpace();
|
|
||||||
} while (readChar(','));
|
|
||||||
if (!readChar(']')) {
|
|
||||||
throw expected("',' or ']'");
|
|
||||||
}
|
|
||||||
nestingLevel--;
|
|
||||||
handler.endArray(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readObject() throws IOException {
|
|
||||||
O object = handler.startObject();
|
|
||||||
read();
|
|
||||||
if (++nestingLevel > MAX_NESTING_LEVEL) {
|
|
||||||
throw error("Nesting too deep");
|
|
||||||
}
|
|
||||||
skipWhiteSpace();
|
|
||||||
if (readChar('}')) {
|
|
||||||
nestingLevel--;
|
|
||||||
handler.endObject(object);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
skipWhiteSpace();
|
|
||||||
handler.startObjectName(object);
|
|
||||||
String name = readName();
|
|
||||||
handler.endObjectName(object, name);
|
|
||||||
skipWhiteSpace();
|
|
||||||
if (!readChar(':')) {
|
|
||||||
throw expected("':'");
|
|
||||||
}
|
|
||||||
skipWhiteSpace();
|
|
||||||
handler.startObjectValue(object, name);
|
|
||||||
readValue();
|
|
||||||
handler.endObjectValue(object, name);
|
|
||||||
skipWhiteSpace();
|
|
||||||
} while (readChar(','));
|
|
||||||
if (!readChar('}')) {
|
|
||||||
throw expected("',' or '}'");
|
|
||||||
}
|
|
||||||
nestingLevel--;
|
|
||||||
handler.endObject(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readName() throws IOException {
|
|
||||||
if (current != '"') {
|
|
||||||
throw expected("name");
|
|
||||||
}
|
|
||||||
return readStringInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readNull() throws IOException {
|
|
||||||
handler.startNull();
|
|
||||||
read();
|
|
||||||
readRequiredChar('u');
|
|
||||||
readRequiredChar('l');
|
|
||||||
readRequiredChar('l');
|
|
||||||
handler.endNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readTrue() throws IOException {
|
|
||||||
handler.startBoolean();
|
|
||||||
read();
|
|
||||||
readRequiredChar('r');
|
|
||||||
readRequiredChar('u');
|
|
||||||
readRequiredChar('e');
|
|
||||||
handler.endBoolean(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFalse() throws IOException {
|
|
||||||
handler.startBoolean();
|
|
||||||
read();
|
|
||||||
readRequiredChar('a');
|
|
||||||
readRequiredChar('l');
|
|
||||||
readRequiredChar('s');
|
|
||||||
readRequiredChar('e');
|
|
||||||
handler.endBoolean(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readRequiredChar(char ch) throws IOException {
|
|
||||||
if (!readChar(ch)) {
|
|
||||||
throw expected("'" + ch + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readString() throws IOException {
|
|
||||||
handler.startString();
|
|
||||||
handler.endString(readStringInternal());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readStringInternal() throws IOException {
|
|
||||||
read();
|
|
||||||
startCapture();
|
|
||||||
while (current != '"') {
|
|
||||||
if (current == '\\') {
|
|
||||||
pauseCapture();
|
|
||||||
readEscape();
|
|
||||||
startCapture();
|
|
||||||
} else if (current < 0x20) {
|
|
||||||
throw expected("valid string character");
|
|
||||||
} else {
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String string = endCapture();
|
|
||||||
read();
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readEscape() throws IOException {
|
|
||||||
read();
|
|
||||||
switch (current) {
|
|
||||||
case '"':
|
|
||||||
case '/':
|
|
||||||
case '\\':
|
|
||||||
captureBuffer.append((char) current);
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
captureBuffer.append('\b');
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
captureBuffer.append('\f');
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
captureBuffer.append('\n');
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
captureBuffer.append('\r');
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
captureBuffer.append('\t');
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
char[] hexChars = new char[4];
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
read();
|
|
||||||
if (!isHexDigit()) {
|
|
||||||
throw expected("hexadecimal digit");
|
|
||||||
}
|
|
||||||
hexChars[i] = (char) current;
|
|
||||||
}
|
|
||||||
captureBuffer.append((char) Integer.parseInt(new String(hexChars), 16));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw expected("valid escape sequence");
|
|
||||||
}
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readNumber() throws IOException {
|
|
||||||
handler.startNumber();
|
|
||||||
startCapture();
|
|
||||||
readChar('-');
|
|
||||||
int firstDigit = current;
|
|
||||||
if (!readDigit()) {
|
|
||||||
throw expected("digit");
|
|
||||||
}
|
|
||||||
if (firstDigit != '0') {
|
|
||||||
while (true) {
|
|
||||||
if (!readDigit()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readFraction();
|
|
||||||
readExponent();
|
|
||||||
handler.endNumber(endCapture());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFraction() throws IOException {
|
|
||||||
if (!readChar('.')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!readDigit()) {
|
|
||||||
throw expected("digit");
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
if (!readDigit()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readExponent() throws IOException {
|
|
||||||
if (!readChar('e') && !readChar('E')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!readChar('+')) {
|
|
||||||
readChar('-');
|
|
||||||
}
|
|
||||||
if (!readDigit()) {
|
|
||||||
throw expected("digit");
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
if (!readDigit()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean readChar(char ch) throws IOException {
|
|
||||||
if (current != ch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
read();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean readDigit() throws IOException {
|
|
||||||
if (!isDigit()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
read();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void skipWhiteSpace() throws IOException {
|
|
||||||
while (isWhiteSpace()) {
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void read() throws IOException {
|
|
||||||
if (index == fill) {
|
|
||||||
if (captureStart != -1) {
|
|
||||||
captureBuffer.append(buffer, captureStart, fill - captureStart);
|
|
||||||
captureStart = 0;
|
|
||||||
}
|
|
||||||
fill = reader.read(buffer, 0, buffer.length);
|
|
||||||
index = 0;
|
|
||||||
if (fill == -1) {
|
|
||||||
current = -1;
|
|
||||||
index++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current = buffer[index++];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startCapture() {
|
|
||||||
if (captureBuffer == null) {
|
|
||||||
captureBuffer = new StringBuilder();
|
|
||||||
}
|
|
||||||
captureStart = index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pauseCapture() {
|
|
||||||
int end = current == -1 ? index : index - 1;
|
|
||||||
captureBuffer.append(buffer, captureStart, end - captureStart);
|
|
||||||
captureStart = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String endCapture() {
|
|
||||||
int start = captureStart;
|
|
||||||
int end = index - 1;
|
|
||||||
captureStart = -1;
|
|
||||||
if (captureBuffer.length() > 0) {
|
|
||||||
captureBuffer.append(buffer, start, end - start);
|
|
||||||
String captured = captureBuffer.toString();
|
|
||||||
captureBuffer.setLength(0);
|
|
||||||
return captured;
|
|
||||||
}
|
|
||||||
return new String(buffer, start, end - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonException expected(String expected) {
|
|
||||||
if (isEndOfText()) {
|
|
||||||
return error("Unexpected end of input");
|
|
||||||
}
|
|
||||||
return error("Expected " + expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonException error(String message) {
|
|
||||||
return new JsonException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isWhiteSpace() {
|
|
||||||
return current == ' ' || current == '\t' || current == '\n' || current == '\r';
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDigit() {
|
|
||||||
return current >= '0' && current <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isHexDigit() {
|
|
||||||
return current >= '0' && current <= '9'
|
|
||||||
|| current >= 'a' && current <= 'f'
|
|
||||||
|| current >= 'A' && current <= 'F';
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isEndOfText() {
|
|
||||||
return current == -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class JsonString extends JsonValue {
|
|
||||||
|
|
||||||
private final String string;
|
|
||||||
|
|
||||||
JsonString(String string) {
|
|
||||||
Objects.requireNonNull(string);
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(JsonWriter writer) throws IOException {
|
|
||||||
writer.writeString(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isString() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String asString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return string.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
return this == object || object != null && getClass() == object.getClass()
|
|
||||||
&& string.equals(((JsonString) object).string);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,295 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a JSON value. This can be a JSON <strong>object</strong>, an <strong> array</strong>,
|
|
||||||
* a <strong>number</strong>, a <strong>string</strong>, or one of the literals
|
|
||||||
* <strong>true</strong>, <strong>false</strong>, and <strong>null</strong>.
|
|
||||||
* <p>
|
|
||||||
* The literals <strong>true</strong>, <strong>false</strong>, and <strong>null</strong> are
|
|
||||||
* represented by the constants
|
|
||||||
* {@code JsonLiteral.NULL}, {@code JsonLiteral.FALSE}, and {@code JsonLiteral.NULL}.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* JSON <strong>objects</strong> and <strong>arrays</strong> are represented by the subtypes
|
|
||||||
* {@link JsonObject} and {@link JsonArray}. Instances of these types can be created using the
|
|
||||||
* public constructors of these classes.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Instances that represent JSON <strong>numbers</strong>, <strong>strings</strong> and
|
|
||||||
* <strong>boolean</strong> values can be created using the static factory methods
|
|
||||||
* {@code JSON.parse(String)}, {@code valueOf(long)}, {@code valueOf(double)}, etc.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* In order to find out whether an instance of this class is of a certain type, the methods
|
|
||||||
* {@link #isObject()}, {@link #isArray()}, {@link #isString()}, {@link #isInt()} etc. can be
|
|
||||||
* used.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* If the type of a JSON value is known, the methods {@link #asObject()}, {@link #asArray()},
|
|
||||||
* {@link #asString()}, {@link #asInt()}, etc. can be used to get this value directly in the
|
|
||||||
* appropriate target type.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public abstract class JsonValue {
|
|
||||||
|
|
||||||
public JsonValue() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON object. If this is the case, this value is an
|
|
||||||
* instance of {@link JsonObject}.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value is an instance of JsonObject
|
|
||||||
*/
|
|
||||||
public boolean isObject() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON array. If this is the case, this value is an
|
|
||||||
* instance of {@link JsonArray}.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value is an instance of JsonArray
|
|
||||||
*/
|
|
||||||
public boolean isArray() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON number that is an integer.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents a JSON number that is an integer
|
|
||||||
*/
|
|
||||||
public boolean isInt() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON number that is an long.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents a JSON number that is an long
|
|
||||||
*/
|
|
||||||
public boolean isLong() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON number that is an float.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents a JSON number that is an float
|
|
||||||
*/
|
|
||||||
public boolean isFloat() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON number that is an double.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents a JSON number that is an double
|
|
||||||
*/
|
|
||||||
public boolean isDouble() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a JSON string.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents a JSON string
|
|
||||||
*/
|
|
||||||
public boolean isString() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents a boolean value.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents either the JSON literal <code>true</code> or
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean isBoolean() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents the JSON literal <code>true</code>.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents the JSON literal <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean isTrue() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents the JSON literal <code>false</code>.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents the JSON literal <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean isFalse() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether this value represents the JSON literal <code>null</code>.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this value represents the JSON literal <code>null</code>
|
|
||||||
*/
|
|
||||||
public boolean isNull() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as {@link JsonObject}, assuming that this value represents a JSON
|
|
||||||
* object. If this is not the case, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @return a JSONObject for this value
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON object
|
|
||||||
*/
|
|
||||||
public JsonObject asObject() {
|
|
||||||
throw new UnsupportedOperationException("Not an object: " + toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as {@link JsonArray}, assuming that this value represents a JSON array.
|
|
||||||
* If this is not the case, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @return a JSONArray for this value
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON array
|
|
||||||
*/
|
|
||||||
public JsonArray asArray() {
|
|
||||||
throw new UnsupportedOperationException("Not an array: " + toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as an <code>int</code> value, assuming that this value represents a
|
|
||||||
* JSON number that can be interpreted as Java <code>int</code>. If this is not the case, an
|
|
||||||
* exception is thrown.
|
|
||||||
* <p>
|
|
||||||
* To be interpreted as Java <code>int</code>, the JSON number must neither contain an exponent
|
|
||||||
* nor a fraction part. Moreover, the number must be in the <code>Integer</code> range.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return this value as <code>int</code>
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON number
|
|
||||||
* @throws NumberFormatException if this JSON number can not be interpreted as <code>int</code> value
|
|
||||||
*/
|
|
||||||
public int asInt() {
|
|
||||||
throw new UnsupportedOperationException("Not a number: " + toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as a <code>long</code> value, assuming that this value represents a
|
|
||||||
* JSON number that can be interpreted as Java <code>long</code>. If this is not the case, an
|
|
||||||
* exception is thrown.
|
|
||||||
* <p>
|
|
||||||
* To be interpreted as Java <code>long</code>, the JSON number must neither contain an exponent
|
|
||||||
* nor a fraction part. Moreover, the number must be in the <code>Long</code> range.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return this value as <code>long</code>
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON number
|
|
||||||
* @throws NumberFormatException if this JSON number can not be interpreted as <code>long</code> value
|
|
||||||
*/
|
|
||||||
public long asLong() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as a <code>float</code> value, assuming that this value represents a
|
|
||||||
* JSON number. If this is not the case, an exception is thrown.
|
|
||||||
* <p>
|
|
||||||
* If the JSON number is out of the <code>Float</code> range, {@link Float#POSITIVE_INFINITY} or
|
|
||||||
* {@link Float#NEGATIVE_INFINITY} is returned.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return this value as <code>float</code>
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON number
|
|
||||||
*/
|
|
||||||
public float asFloat() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as a <code>double</code> value, assuming that this value represents a
|
|
||||||
* JSON number. If this is not the case, an exception is thrown.
|
|
||||||
* <p>
|
|
||||||
* If the JSON number is out of the <code>Double</code> range, {@link Double#POSITIVE_INFINITY} or
|
|
||||||
* {@link Double#NEGATIVE_INFINITY} is returned.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return this value as <code>double</code>
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON number
|
|
||||||
*/
|
|
||||||
public double asDouble() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as String, assuming that this value represents a JSON string. If this
|
|
||||||
* is not the case, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @return the string represented by this value
|
|
||||||
* @throws UnsupportedOperationException if this value is not a JSON string
|
|
||||||
*/
|
|
||||||
public String asString() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this JSON value as a <code>boolean</code> value, assuming that this value is either
|
|
||||||
* <code>true</code> or <code>false</code>. If this is not the case, an exception is thrown.
|
|
||||||
*
|
|
||||||
* @return this value as <code>boolean</code>
|
|
||||||
* @throws UnsupportedOperationException if this value is neither <code>true</code> or <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean asBoolean() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the JSON string for this value in its minimal form, without any additional whitespace.
|
|
||||||
*
|
|
||||||
* @return a JSON string that represents this value
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return toString(JsonWriterConfig.minimal());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the JSON string for this value using the given formatting.
|
|
||||||
*
|
|
||||||
* @param config a configuration that controls the formatting or <code>null</code> for the minimal form
|
|
||||||
* @return a JSON string that represents this value
|
|
||||||
*/
|
|
||||||
public String toString(JsonWriterConfig config) {
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
try {
|
|
||||||
write(config.createWriter(writer));
|
|
||||||
} catch (IOException exception) {
|
|
||||||
// StringWriter does not throw IOException, so this is impossible
|
|
||||||
throw new JsonException(exception);
|
|
||||||
}
|
|
||||||
return writer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract void write(JsonWriter writer) throws IOException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class JsonWriter {
|
|
||||||
|
|
||||||
private static final int CONTROL_CHARACTERS_END = 0x001f;
|
|
||||||
|
|
||||||
private static final char[] QUOT_CHARS = {'\\', '"'};
|
|
||||||
private static final char[] BS_CHARS = {'\\', '\\'};
|
|
||||||
private static final char[] LF_CHARS = {'\\', 'n'};
|
|
||||||
private static final char[] CR_CHARS = {'\\', 'r'};
|
|
||||||
private static final char[] TAB_CHARS = {'\\', 't'};
|
|
||||||
// In JavaScript, U+2028 and U+2029 characters count as line endings and must be encoded.
|
|
||||||
// http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character
|
|
||||||
private static final char[] UNICODE_2028_CHARS = {'\\', 'u', '2', '0', '2', '8'};
|
|
||||||
private static final char[] UNICODE_2029_CHARS = {'\\', 'u', '2', '0', '2', '9'};
|
|
||||||
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
||||||
'a', 'b', 'c', 'd', 'e', 'f'};
|
|
||||||
|
|
||||||
protected final Writer writer;
|
|
||||||
|
|
||||||
public JsonWriter(Writer writer) {
|
|
||||||
this.writer = writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeLiteral(String value) throws IOException {
|
|
||||||
writer.write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeNumber(String string) throws IOException {
|
|
||||||
writer.write(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeString(String string) throws IOException {
|
|
||||||
writer.write('"');
|
|
||||||
writeJsonString(string);
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeArrayOpen() throws IOException {
|
|
||||||
writer.write('[');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeArrayClose() throws IOException {
|
|
||||||
writer.write(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeArraySeparator() throws IOException {
|
|
||||||
writer.write(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeObjectOpen() throws IOException {
|
|
||||||
writer.write('{');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeObjectClose() throws IOException {
|
|
||||||
writer.write('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeMemberName(String name) throws IOException {
|
|
||||||
writer.write('"');
|
|
||||||
writeJsonString(name);
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeMemberSeparator() throws IOException {
|
|
||||||
writer.write(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeObjectSeparator() throws IOException {
|
|
||||||
writer.write(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeJsonString(String string) throws IOException {
|
|
||||||
int length = string.length();
|
|
||||||
int start = 0;
|
|
||||||
for (int index = 0; index < length; index++) {
|
|
||||||
char[] replacement = getReplacementChars(string.charAt(index));
|
|
||||||
if (replacement != null) {
|
|
||||||
writer.write(string, start, index - start);
|
|
||||||
writer.write(replacement);
|
|
||||||
start = index + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.write(string, start, length - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static char[] getReplacementChars(char ch) {
|
|
||||||
if (ch > '\\') {
|
|
||||||
if (ch < '\u2028' || ch > '\u2029') {
|
|
||||||
// The lower range contains 'a' .. 'z'. Only 2 checks required.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ch == '\u2028' ? UNICODE_2028_CHARS : UNICODE_2029_CHARS;
|
|
||||||
}
|
|
||||||
if (ch == '\\') {
|
|
||||||
return BS_CHARS;
|
|
||||||
}
|
|
||||||
if (ch > '"') {
|
|
||||||
// This range contains '0' .. '9' and 'A' .. 'Z'. Need 3 checks to get here.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (ch == '"') {
|
|
||||||
return QUOT_CHARS;
|
|
||||||
}
|
|
||||||
if (ch > CONTROL_CHARACTERS_END) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (ch == '\n') {
|
|
||||||
return LF_CHARS;
|
|
||||||
}
|
|
||||||
if (ch == '\r') {
|
|
||||||
return CR_CHARS;
|
|
||||||
}
|
|
||||||
if (ch == '\t') {
|
|
||||||
return TAB_CHARS;
|
|
||||||
}
|
|
||||||
return new char[]{'\\', 'u', '0', '0', HEX_DIGITS[ch >> 4 & 0x000f], HEX_DIGITS[ch & 0x000f]};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the formatting of the JSON output. Use one of the available constants.
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface JsonWriterConfig {
|
|
||||||
|
|
||||||
JsonWriter createWriter(Writer writer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write JSON in its minimal form, without any additional whitespace. This is the default.
|
|
||||||
*/
|
|
||||||
static JsonWriterConfig minimal() {
|
|
||||||
return JsonWriter::new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write JSON in pretty-print, with each value on a separate line and an indentation of two
|
|
||||||
* spaces.
|
|
||||||
*/
|
|
||||||
static JsonWriterConfig prettyPrint(int n) {
|
|
||||||
return new PrettyPrint(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables human readable JSON output by inserting whitespace between values.after commas and
|
|
||||||
* colons. Example:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* jsonValue.writeTo(writer, WriterConfig.prettyPrint());
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
class PrettyPrint implements JsonWriterConfig {
|
|
||||||
|
|
||||||
private final char[] indentChars;
|
|
||||||
|
|
||||||
PrettyPrint(char[] indentChars) {
|
|
||||||
this.indentChars = indentChars;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print every value on a separate line. Use the given number of spaces for indentation.
|
|
||||||
*
|
|
||||||
* @param number the number of spaces to use
|
|
||||||
*/
|
|
||||||
PrettyPrint(int number) {
|
|
||||||
this(fillChars(number));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static char[] fillChars(int number) {
|
|
||||||
if (number < 0) {
|
|
||||||
throw new IllegalArgumentException("number is negative");
|
|
||||||
}
|
|
||||||
char[] chars = new char[number];
|
|
||||||
Arrays.fill(chars, ' ');
|
|
||||||
return chars;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonWriter createWriter(Writer writer) {
|
|
||||||
return new PrettyPrintWriter(writer, indentChars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PrettyPrintWriter extends JsonWriter {
|
|
||||||
|
|
||||||
private final char[] indentChars;
|
|
||||||
private int indent;
|
|
||||||
|
|
||||||
private PrettyPrintWriter(Writer writer, char[] indentChars) {
|
|
||||||
super(writer);
|
|
||||||
this.indentChars = indentChars;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeArrayOpen() throws IOException {
|
|
||||||
indent++;
|
|
||||||
writer.write('[');
|
|
||||||
writeNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeArrayClose() throws IOException {
|
|
||||||
indent--;
|
|
||||||
writeNewLine();
|
|
||||||
writer.write(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeArraySeparator() throws IOException {
|
|
||||||
writer.write(',');
|
|
||||||
if (!writeNewLine()) {
|
|
||||||
writer.write(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeObjectOpen() throws IOException {
|
|
||||||
indent++;
|
|
||||||
writer.write('{');
|
|
||||||
writeNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeObjectClose() throws IOException {
|
|
||||||
indent--;
|
|
||||||
writeNewLine();
|
|
||||||
writer.write('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeMemberSeparator() throws IOException {
|
|
||||||
writer.write(':');
|
|
||||||
writer.write(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeObjectSeparator() throws IOException {
|
|
||||||
writer.write(',');
|
|
||||||
if (!writeNewLine()) {
|
|
||||||
writer.write(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean writeNewLine() throws IOException {
|
|
||||||
if (indentChars == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
writer.write('\n');
|
|
||||||
for (int i = 0; i < indent; i++) {
|
|
||||||
writer.write(indentChars);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -67,7 +67,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
|
|
||||||
private Writer writer;
|
private Writer writer;
|
||||||
|
|
||||||
private JsonWriter jsonWriter;
|
private JsonBuilder jsonBuilder;
|
||||||
|
|
||||||
private Marc.Builder builder;
|
private Marc.Builder builder;
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
private String index;
|
private String index;
|
||||||
|
|
||||||
private String indexType;
|
private String indexType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag for indicating if writer is at top of file.
|
* Flag for indicating if writer is at top of file.
|
||||||
*/
|
*/
|
||||||
|
@ -111,7 +112,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
|
|
||||||
public MarcJsonWriter(Writer writer, int bufferSize) {
|
public MarcJsonWriter(Writer writer, int bufferSize) {
|
||||||
this.writer = new BufferedWriter(writer, bufferSize);
|
this.writer = new BufferedWriter(writer, bufferSize);
|
||||||
this.jsonWriter = new JsonWriter(this.writer);
|
this.jsonBuilder = new JsonBuilder(this.writer);
|
||||||
this.bufferSize = bufferSize;
|
this.bufferSize = bufferSize;
|
||||||
this.lock = new ReentrantLock();
|
this.lock = new ReentrantLock();
|
||||||
this.builder = Marc.builder();
|
this.builder = Marc.builder();
|
||||||
|
@ -143,6 +144,10 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JsonBuilder getJsonBuilder() {
|
||||||
|
return jsonBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
public MarcJsonWriter setIndex(String index, String indexType) {
|
public MarcJsonWriter setIndex(String index, String indexType) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.indexType = indexType;
|
this.indexType = indexType;
|
||||||
|
@ -182,14 +187,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startDocument() {
|
public void startDocument() {
|
||||||
// nothing to do here
|
if (style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
|
try {
|
||||||
|
jsonBuilder.beginMap();
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginCollection() {
|
public void beginCollection() {
|
||||||
if (style.contains(Style.ARRAY)) {
|
if (style.contains(Style.ARRAY)) {
|
||||||
try {
|
try {
|
||||||
jsonWriter.writeArrayOpen();
|
jsonBuilder.beginCollection();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
handleException(e);
|
handleException(e);
|
||||||
}
|
}
|
||||||
|
@ -257,13 +268,13 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
public void endCollection() {
|
public void endCollection() {
|
||||||
if (style.contains(Style.ARRAY)) {
|
if (style.contains(Style.ARRAY)) {
|
||||||
try {
|
try {
|
||||||
jsonWriter.writeArrayClose();
|
jsonBuilder.endCollection();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
handleException(e);
|
handleException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||||
// finish with line-feed "\n", not with System.lineSeparator()
|
// finish with line-feed "\n", not with System.lineSeparator() !!!
|
||||||
try {
|
try {
|
||||||
writer.write("\n");
|
writer.write("\n");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -279,13 +290,25 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endDocument() {
|
public void endDocument() {
|
||||||
try {
|
if (style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
flush();
|
try {
|
||||||
} catch (IOException e) {
|
jsonBuilder.endMap();
|
||||||
handleException(e);
|
} catch (IOException e) {
|
||||||
|
handleException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(Map<String, Object> map) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write MARC record using fields, indicators, and subfield structures,
|
* Write MARC record using fields, indicators, and subfield structures,
|
||||||
* therefore allowing duplicate keys in the output.
|
* therefore allowing duplicate keys in the output.
|
||||||
|
@ -303,67 +326,47 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (style.contains(Style.ARRAY)) {
|
if (style.contains(Style.ARRAY)) {
|
||||||
jsonWriter.writeArraySeparator();
|
writer.write(",");
|
||||||
} else if (style.contains(Style.LINES)) {
|
} else if (style.contains(Style.LINES)) {
|
||||||
jsonWriter.writeLiteral("\n");
|
writer.append(System.lineSeparator());
|
||||||
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||||
jsonWriter.writeLiteral("\n");
|
writer.append(System.lineSeparator());
|
||||||
writeMetaDataLine(marcRecord);
|
writeMetaDataLine(marcRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonWriter.writeObjectOpen();
|
if (!style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
|
jsonBuilder.beginMap();
|
||||||
|
}
|
||||||
if (marcRecord.getFormat() != null) {
|
if (marcRecord.getFormat() != null) {
|
||||||
jsonWriter.writeMemberName(FORMAT_TAG);
|
jsonBuilder.buildKey(FORMAT_TAG).buildValue(marcRecord.getFormat());
|
||||||
jsonWriter.writeMemberSeparator();
|
|
||||||
jsonWriter.writeString(marcRecord.getFormat());
|
|
||||||
jsonWriter.writeObjectSeparator();
|
|
||||||
}
|
}
|
||||||
if (marcRecord.getType() != null) {
|
if (marcRecord.getType() != null) {
|
||||||
jsonWriter.writeMemberName(TYPE_TAG);
|
jsonBuilder.buildKey(TYPE_TAG).buildValue(marcRecord.getType());
|
||||||
jsonWriter.writeMemberSeparator();
|
|
||||||
jsonWriter.writeString(marcRecord.getType());
|
|
||||||
jsonWriter.writeObjectSeparator();
|
|
||||||
}
|
}
|
||||||
if (!RecordLabel.EMPTY.equals(marcRecord.getRecordLabel())) {
|
if (!RecordLabel.EMPTY.equals(marcRecord.getRecordLabel())) {
|
||||||
jsonWriter.writeMemberName(LEADER_TAG);
|
jsonBuilder.buildKey(LEADER_TAG).buildValue(marcRecord.getRecordLabel().toString());
|
||||||
jsonWriter.writeMemberSeparator();
|
|
||||||
jsonWriter.writeString(marcRecord.getRecordLabel().toString());
|
|
||||||
jsonWriter.writeObjectSeparator();
|
|
||||||
}
|
}
|
||||||
boolean fieldseparator = false;
|
|
||||||
for (MarcField marcField : marcRecord.getFields()) {
|
for (MarcField marcField : marcRecord.getFields()) {
|
||||||
if (fieldseparator) {
|
jsonBuilder.buildKey(marcField.getTag());
|
||||||
jsonWriter.writeObjectSeparator();
|
|
||||||
}
|
|
||||||
jsonWriter.writeMemberName(marcField.getTag());
|
|
||||||
jsonWriter.writeMemberSeparator();
|
|
||||||
if (marcField.isControl()) {
|
if (marcField.isControl()) {
|
||||||
jsonWriter.writeArrayOpen();
|
jsonBuilder.buildValue(marcField.recoverControlFieldValue());
|
||||||
jsonWriter.writeString(marcField.getValue());
|
|
||||||
jsonWriter.writeArrayClose();
|
|
||||||
} else {
|
} else {
|
||||||
jsonWriter.writeObjectOpen();
|
jsonBuilder.beginMap();
|
||||||
jsonWriter.writeMemberName(marcField.getIndicator());
|
jsonBuilder.buildKey(marcField.getIndicator());
|
||||||
jsonWriter.writeMemberSeparator();
|
jsonBuilder.beginCollection();
|
||||||
jsonWriter.writeArrayOpen();
|
|
||||||
boolean subfieldseparator = false;
|
|
||||||
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
||||||
if (subfieldseparator) {
|
jsonBuilder.beginMap();
|
||||||
jsonWriter.writeObjectSeparator();
|
jsonBuilder.buildKey(subfield.getId());
|
||||||
}
|
jsonBuilder.buildValue(subfield.getValue());
|
||||||
jsonWriter.writeObjectOpen();
|
jsonBuilder.endMap();
|
||||||
jsonWriter.writeMemberName(subfield.getId());
|
|
||||||
jsonWriter.writeMemberSeparator();
|
|
||||||
jsonWriter.writeString(subfield.getValue());
|
|
||||||
jsonWriter.writeObjectClose();
|
|
||||||
subfieldseparator = true;
|
|
||||||
}
|
}
|
||||||
jsonWriter.writeArrayClose();
|
jsonBuilder.endCollection();
|
||||||
jsonWriter.writeObjectClose();
|
jsonBuilder.endMap();
|
||||||
}
|
}
|
||||||
fieldseparator = true;
|
|
||||||
}
|
}
|
||||||
jsonWriter.writeObjectClose();
|
if (!style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
|
jsonBuilder.endMap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -385,14 +388,16 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
if (style.contains(Style.ARRAY)) {
|
if (style.contains(Style.ARRAY)) {
|
||||||
writer.write(",");
|
writer.write(",");
|
||||||
} else if (style.contains(Style.LINES)) {
|
} else if (style.contains(Style.LINES)) {
|
||||||
writer.write("\n");
|
writer.write(System.lineSeparator());
|
||||||
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||||
writer.write("\n");
|
writer.write(System.lineSeparator());
|
||||||
writeMetaDataLine(marcRecord);
|
writeMetaDataLine(marcRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("{");
|
if (!style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
|
sb.append("{");
|
||||||
|
}
|
||||||
int c0 = 0;
|
int c0 = 0;
|
||||||
for (Map.Entry<String, Object> tags : marcRecord.entrySet()) {
|
for (Map.Entry<String, Object> tags : marcRecord.entrySet()) {
|
||||||
if (c0 > 0) {
|
if (c0 > 0) {
|
||||||
|
@ -514,7 +519,9 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
}
|
}
|
||||||
c0++;
|
c0++;
|
||||||
}
|
}
|
||||||
sb.append('}');
|
if (!style.contains(Style.EMBEDDED_RECORD)) {
|
||||||
|
sb.append('}');
|
||||||
|
}
|
||||||
writer.write(sb.toString());
|
writer.write(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +568,8 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
writer = new OutputStreamWriter(compress ?
|
writer = new OutputStreamWriter(compress ?
|
||||||
new CompressedOutputStream(out, bufferSize) :
|
new CompressedOutputStream(out, bufferSize) :
|
||||||
new BufferedOutputStream(out, bufferSize), StandardCharsets.UTF_8);
|
new BufferedOutputStream(out, bufferSize), StandardCharsets.UTF_8);
|
||||||
jsonWriter = new JsonWriter(writer);
|
//jsonWriter = new JsonWriter(writer);
|
||||||
|
jsonBuilder = new JsonBuilder(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -582,7 +590,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
"\"_index\":\"" + index + "\"," +
|
"\"_index\":\"" + index + "\"," +
|
||||||
"\"_type\":\"" + indexType + "\"," +
|
"\"_type\":\"" + indexType + "\"," +
|
||||||
"\"_id\":\"" + id + "\"}}" +
|
"\"_id\":\"" + id + "\"}}" +
|
||||||
"\n");
|
System.lineSeparator());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
handleException(e);
|
handleException(e);
|
||||||
}
|
}
|
||||||
|
@ -601,34 +609,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
for (int i = 0; i < value.length(); i++) {
|
for (int i = 0; i < value.length(); i++) {
|
||||||
char c = value.charAt(i);
|
char c = value.charAt(i);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"':
|
case '"' -> sb.append("\\\"");
|
||||||
sb.append("\\\"");
|
case '\\' -> sb.append("\\\\");
|
||||||
break;
|
case '\b' -> sb.append("\\b");
|
||||||
case '\\':
|
case '\f' -> sb.append("\\f");
|
||||||
sb.append("\\\\");
|
case '\n' -> sb.append("\\n");
|
||||||
break;
|
case '\r' -> sb.append("\\r");
|
||||||
case '\b':
|
case '\t' -> sb.append("\\t");
|
||||||
sb.append("\\b");
|
default -> {
|
||||||
break;
|
|
||||||
case '\f':
|
|
||||||
sb.append("\\f");
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
sb.append("\\n");
|
|
||||||
break;
|
|
||||||
case '\r':
|
|
||||||
sb.append("\\r");
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
sb.append("\\t");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (c < 0x1f) {
|
if (c < 0x1f) {
|
||||||
sb.append("\\u").append(String.format("%04x", (int) c));
|
sb.append("\\u").append(String.format("%04x", (int) c));
|
||||||
} else {
|
} else {
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
@ -638,7 +632,11 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum Style {
|
public enum Style {
|
||||||
ARRAY, LINES, ELASTICSEARCH_BULK, ALLOW_DUPLICATES
|
ARRAY,
|
||||||
|
LINES,
|
||||||
|
ELASTICSEARCH_BULK,
|
||||||
|
ALLOW_DUPLICATES,
|
||||||
|
EMBEDDED_RECORD
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -651,5 +649,4 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
||||||
def.setLevel(Deflater.BEST_COMPRESSION);
|
def.setLevel(Deflater.BEST_COMPRESSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -350,19 +350,7 @@ public class MarcXchangeWriter extends MarcContentHandler implements Flushable,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (field.isControl()) {
|
if (field.isControl()) {
|
||||||
String value = field.getValue();
|
String value = field.recoverControlFieldValue();
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
// the control field is disguised as a data field, try lookup value in first subfield of "_"
|
|
||||||
value = field.getFirstSubfieldValue("_");
|
|
||||||
// if no value, maybe " "?
|
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
value = field.getFirstSubfieldValue(" ");
|
|
||||||
}
|
|
||||||
// still no value? Then it is some exotic like MAB with subfield "a"?
|
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
value = field.getFirstSubfieldValue("a");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
Iterator<Attribute> attrs = Collections.singletonList(eventFactory.createAttribute(TAG_ATTRIBUTE,
|
Iterator<Attribute> attrs = Collections.singletonList(eventFactory.createAttribute(TAG_ATTRIBUTE,
|
||||||
transform(field.getTag()))).iterator();
|
transform(field.getTag()))).iterator();
|
||||||
|
|
|
@ -1,522 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.inOrder;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.InOrder;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ConcurrentModificationException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class JsonArrayTest {
|
|
||||||
|
|
||||||
private JsonArray array;
|
|
||||||
|
|
||||||
private static JsonArray array(String... values) {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (String value : values) {
|
|
||||||
array.add(value);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
array = new JsonArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorFailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> new JsonArray(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorHasSameValues() {
|
|
||||||
array.add(23);
|
|
||||||
JsonArray copy = new JsonArray(array);
|
|
||||||
assertEquals(array.values(), copy.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorworksOnSafeCopy() {
|
|
||||||
JsonArray copy = new JsonArray(array);
|
|
||||||
array.add(23);
|
|
||||||
assertTrue(copy.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isEmptyisTrueAfterCreation() {
|
|
||||||
assertTrue(array.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isEmptyisFalseAfterAdd() {
|
|
||||||
array.add(true);
|
|
||||||
assertFalse(array.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sizeisZeroAfterCreation() {
|
|
||||||
assertEquals(0, array.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sizeisOneAfterAdd() {
|
|
||||||
array.add(true);
|
|
||||||
assertEquals(1, array.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratorisEmptyAfterCreation() {
|
|
||||||
assertFalse(array.iterator().hasNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratorhasNextAfterAdd() {
|
|
||||||
array.add(true);
|
|
||||||
Iterator<JsonValue> iterator = array.iterator();
|
|
||||||
assertTrue(iterator.hasNext());
|
|
||||||
assertEquals(JsonLiteral.TRUE, iterator.next());
|
|
||||||
assertFalse(iterator.hasNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratordoesNotAllowModification() {
|
|
||||||
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
|
|
||||||
array.add(23);
|
|
||||||
Iterator<JsonValue> iterator = array.iterator();
|
|
||||||
iterator.next();
|
|
||||||
iterator.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratordetectsConcurrentModification() {
|
|
||||||
Assertions.assertThrows(ConcurrentModificationException.class, () -> {
|
|
||||||
Iterator<JsonValue> iterator = array.iterator();
|
|
||||||
array.add(23);
|
|
||||||
iterator.next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuesisEmptyAfterCreation() {
|
|
||||||
assertTrue(array.values().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuescontainsValueAfterAdd() {
|
|
||||||
array.add(true);
|
|
||||||
assertEquals(1, array.values().size());
|
|
||||||
assertEquals(JsonLiteral.TRUE, array.values().get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuesReflectsChanges() {
|
|
||||||
List<JsonValue> values = array.values();
|
|
||||||
array.add(true);
|
|
||||||
assertEquals(array.values(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuesPreventsModification() {
|
|
||||||
List<JsonValue> values = array.values();
|
|
||||||
values.add(JsonLiteral.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getreturnsValue() {
|
|
||||||
array.add(23);
|
|
||||||
JsonValue value = array.get(0);
|
|
||||||
assertEquals(Json.of(23), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getfailsWithInvalidIndex() {
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> array.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addint() {
|
|
||||||
array.add(23);
|
|
||||||
assertEquals("[23]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addintenablesChaining() {
|
|
||||||
assertSame(array, array.add(23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addlong() {
|
|
||||||
array.add(23L);
|
|
||||||
assertEquals("[23]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addlongenablesChaining() {
|
|
||||||
assertSame(array, array.add(23L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addfloat() {
|
|
||||||
array.add(3.14f);
|
|
||||||
assertEquals("[3.14]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addfloatenablesChaining() {
|
|
||||||
assertSame(array, array.add(3.14f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void adddouble() {
|
|
||||||
array.add(3.14d);
|
|
||||||
assertEquals("[3.14]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void adddoubleenablesChaining() {
|
|
||||||
assertSame(array, array.add(3.14d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addboolean() {
|
|
||||||
array.add(true);
|
|
||||||
assertEquals("[true]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addbooleanenablesChaining() {
|
|
||||||
assertSame(array, array.add(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstring() {
|
|
||||||
array.add("foo");
|
|
||||||
assertEquals("[\"foo\"]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstringenablesChaining() {
|
|
||||||
assertSame(array, array.add("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstringtoleratesNull() {
|
|
||||||
array.add((String) null);
|
|
||||||
assertEquals("[null]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonNull() {
|
|
||||||
array.add(JsonLiteral.NULL);
|
|
||||||
assertEquals("[null]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonArray() {
|
|
||||||
array.add(new JsonArray());
|
|
||||||
assertEquals("[[]]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonObject() {
|
|
||||||
array.add(new JsonObject());
|
|
||||||
assertEquals("[{}]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonenablesChaining() {
|
|
||||||
assertSame(array, array.add(JsonLiteral.NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> array.add((JsonValue) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedArray() {
|
|
||||||
JsonArray innerArray = new JsonArray();
|
|
||||||
innerArray.add(23);
|
|
||||||
array.add(innerArray);
|
|
||||||
assertEquals("[[23]]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedArraymodifiedAfterAdd() {
|
|
||||||
JsonArray innerArray = new JsonArray();
|
|
||||||
array.add(innerArray);
|
|
||||||
innerArray.add(23);
|
|
||||||
assertEquals("[[23]]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedObject() {
|
|
||||||
JsonObject innerObject = new JsonObject();
|
|
||||||
innerObject.add("a", 23);
|
|
||||||
array.add(innerObject);
|
|
||||||
assertEquals("[{\"a\":23}]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedObjectmodifiedAfterAdd() {
|
|
||||||
JsonObject innerObject = new JsonObject();
|
|
||||||
array.add(innerObject);
|
|
||||||
innerObject.add("a", 23);
|
|
||||||
assertEquals("[{\"a\":23}]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setint() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, 23);
|
|
||||||
assertEquals("[23]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setintenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setlong() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, 23L);
|
|
||||||
assertEquals("[23]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setlongenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, 23L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setfloat() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, 3.14f);
|
|
||||||
assertEquals("[3.14]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setfloatenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, 3.14f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setdouble() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, 3.14d);
|
|
||||||
assertEquals("[3.14]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setdoubleenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, 3.14d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setboolean() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, true);
|
|
||||||
assertEquals("[true]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setbooleanenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setstring() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, "foo");
|
|
||||||
assertEquals("[\"foo\"]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setstringenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, "foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonNull() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, JsonLiteral.NULL);
|
|
||||||
assertEquals("[null]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonArray() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, new JsonArray());
|
|
||||||
assertEquals("[[]]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonObject() {
|
|
||||||
array.add(false);
|
|
||||||
array.set(0, new JsonObject());
|
|
||||||
assertEquals("[{}]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setJsonFailsWithNull() {
|
|
||||||
array.add(false);
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () ->
|
|
||||||
array.set(0, (JsonValue) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonfailsWithInvalidIndex() {
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () ->
|
|
||||||
array.set(0, JsonLiteral.NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonenablesChaining() {
|
|
||||||
array.add(false);
|
|
||||||
assertSame(array, array.set(0, JsonLiteral.NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonreplacesDifferntArrayElements() {
|
|
||||||
array.add(3).add(6).add(9);
|
|
||||||
array.set(1, 4).set(2, 5);
|
|
||||||
assertEquals("[3,4,5]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removefailsWithInvalidIndex() {
|
|
||||||
Assertions.assertThrows(IndexOutOfBoundsException.class, () ->
|
|
||||||
array.remove(0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeremovesElement() {
|
|
||||||
array.add(23);
|
|
||||||
array.remove(0);
|
|
||||||
assertEquals("[]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removekeepsOtherElements() {
|
|
||||||
array.add("a").add("b").add("c");
|
|
||||||
array.remove(1);
|
|
||||||
assertEquals("[\"a\",\"c\"]", array.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeempty() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
array.write(writer);
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeArrayOpen();
|
|
||||||
inOrder.verify(writer).writeArrayClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writewithSingleValue() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
array.add(23);
|
|
||||||
array.write(writer);
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeArrayOpen();
|
|
||||||
inOrder.verify(writer).writeNumber("23");
|
|
||||||
inOrder.verify(writer).writeArrayClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writewithMultipleValues() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
array.add(23).add("foo").add(false);
|
|
||||||
array.write(writer);
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeArrayOpen();
|
|
||||||
inOrder.verify(writer).writeNumber("23");
|
|
||||||
inOrder.verify(writer).writeArraySeparator();
|
|
||||||
inOrder.verify(writer).writeString("foo");
|
|
||||||
inOrder.verify(writer).writeArraySeparator();
|
|
||||||
inOrder.verify(writer).writeLiteral("false");
|
|
||||||
inOrder.verify(writer).writeArrayClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isArray() {
|
|
||||||
assertTrue(array.isArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asArray() {
|
|
||||||
assertSame(array, array.asArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalstrueForEqualArrays() {
|
|
||||||
assertEquals(array(), array());
|
|
||||||
assertEquals(array("foo", "bar"), array("foo", "bar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForDifferentArrays() {
|
|
||||||
assertNotEquals(array("foo", "bar"), array("foo", "bar", "baz"));
|
|
||||||
assertNotEquals(array("foo", "bar"), array("bar", "foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForNull() {
|
|
||||||
assertNotEquals(null, array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForSubclass() {
|
|
||||||
assertNotEquals(array, new JsonArray(array) {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodeequalsForEqualArrays() {
|
|
||||||
assertEquals(array().hashCode(), array().hashCode());
|
|
||||||
assertEquals(array("foo").hashCode(), array("foo").hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodediffersForDifferentArrays() {
|
|
||||||
assertNotEquals(array().hashCode(), array("bar").hashCode());
|
|
||||||
assertNotEquals(array("foo").hashCode(), array("bar").hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,17 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package org.xbib.marc.json;
|
package org.xbib.marc.json;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class JsonWriterTest {
|
public class JsonBuilderTest {
|
||||||
|
|
||||||
private StringWriter output;
|
private StringWriter output;
|
||||||
|
|
||||||
private JsonWriter writer;
|
private JsonBuilder jsonBuilder;
|
||||||
|
|
||||||
private static String string(char... chars) {
|
private static String string(char... chars) {
|
||||||
return String.valueOf(chars);
|
return String.valueOf(chars);
|
||||||
|
@ -34,125 +34,110 @@ public class JsonWriterTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
output = new StringWriter();
|
output = new StringWriter();
|
||||||
writer = new JsonWriter(output);
|
jsonBuilder = new JsonBuilder(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeLiteral() throws IOException {
|
public void writeLiteral() throws IOException {
|
||||||
writer.writeLiteral("foo");
|
jsonBuilder.buildValue("foo");
|
||||||
assertEquals("foo", output.toString());
|
assertEquals("\"foo\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeNumber() throws IOException {
|
public void writeNumber() throws IOException {
|
||||||
writer.writeNumber("23");
|
jsonBuilder.buildValue(23);
|
||||||
assertEquals("23", output.toString());
|
assertEquals("23", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeStringempty() throws IOException {
|
public void writeStringEmpty() throws IOException {
|
||||||
writer.writeString("");
|
jsonBuilder.buildValue("");
|
||||||
assertEquals("\"\"", output.toString());
|
assertEquals("\"\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeStingescapesBackslashes() throws IOException {
|
public void writeStingescapesBackslashes() throws IOException {
|
||||||
writer.writeString("foo\\bar");
|
jsonBuilder.buildValue("foo\\bar");
|
||||||
assertEquals("\"foo\\\\bar\"", output.toString());
|
assertEquals("\"foo\\\\bar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeArrayParts() throws IOException {
|
public void writeEmptyArray() throws IOException {
|
||||||
writer.writeArrayOpen();
|
jsonBuilder.beginCollection();
|
||||||
writer.writeArraySeparator();
|
jsonBuilder.endCollection();
|
||||||
writer.writeArrayClose();
|
assertEquals("[]", output.toString());
|
||||||
assertEquals("[,]", output.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeObjectParts() throws IOException {
|
public void writeEmptyObject() throws IOException {
|
||||||
writer.writeObjectOpen();
|
jsonBuilder.beginMap();
|
||||||
writer.writeMemberSeparator();
|
jsonBuilder.endMap();
|
||||||
writer.writeObjectSeparator();
|
assertEquals("{}", output.toString());
|
||||||
writer.writeObjectClose();
|
|
||||||
assertEquals("{:,}", output.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeMemberNameempty() throws IOException {
|
|
||||||
writer.writeMemberName("");
|
|
||||||
assertEquals("\"\"", output.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeMemberNameescapesBackslashes() throws IOException {
|
|
||||||
writer.writeMemberName("foo\\bar");
|
|
||||||
assertEquals("\"foo\\\\bar\"", output.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesQuotes() throws IOException {
|
public void escapesQuotes() throws IOException {
|
||||||
writer.writeString("a\"b");
|
jsonBuilder.buildValue("a\"b");
|
||||||
assertEquals("\"a\\\"b\"", output.toString());
|
assertEquals("\"a\\\"b\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesEscapedQuotes() throws IOException {
|
public void escapesEscapedQuotes() throws IOException {
|
||||||
writer.writeString("foo\\\"bar");
|
jsonBuilder.buildValue("foo\\\"bar");
|
||||||
assertEquals("\"foo\\\\\\\"bar\"", output.toString());
|
assertEquals("\"foo\\\\\\\"bar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesNewLine() throws IOException {
|
public void escapesNewLine() throws IOException {
|
||||||
writer.writeString("foo\nbar");
|
jsonBuilder.buildValue("foo\nbar");
|
||||||
assertEquals("\"foo\\nbar\"", output.toString());
|
assertEquals("\"foo\\nbar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesWindowsNewLine() throws IOException {
|
public void escapesWindowsNewLine() throws IOException {
|
||||||
writer.writeString("foo\r\nbar");
|
jsonBuilder.buildValue("foo\r\nbar");
|
||||||
assertEquals("\"foo\\r\\nbar\"", output.toString());
|
assertEquals("\"foo\\r\\nbar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesTabs() throws IOException {
|
public void escapesTabs() throws IOException {
|
||||||
writer.writeString("foo\tbar");
|
jsonBuilder.buildValue("foo\tbar");
|
||||||
assertEquals("\"foo\\tbar\"", output.toString());
|
assertEquals("\"foo\\tbar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesSpecialCharacters() throws IOException {
|
public void escapesSpecialCharacters() throws IOException {
|
||||||
writer.writeString("foo\u2028bar\u2029");
|
jsonBuilder.buildValue("foo\u2028bar\u2029");
|
||||||
assertEquals("\"foo\\u2028bar\\u2029\"", output.toString());
|
assertEquals("\"foo\\u2028bar\\u2029\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesZeroCharacter() throws IOException {
|
public void escapesZeroCharacter() throws IOException {
|
||||||
writer.writeString(string('f', 'o', 'o', (char) 0, 'b', 'a', 'r'));
|
jsonBuilder.buildValue(string('f', 'o', 'o', (char) 0, 'b', 'a', 'r'));
|
||||||
assertEquals("\"foo\\u0000bar\"", output.toString());
|
assertEquals("\"foo\\u0000bar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesEscapeCharacter() throws IOException {
|
public void escapesEscapeCharacter() throws IOException {
|
||||||
writer.writeString(string('f', 'o', 'o', (char) 27, 'b', 'a', 'r'));
|
jsonBuilder.buildValue(string('f', 'o', 'o', (char) 27, 'b', 'a', 'r'));
|
||||||
assertEquals("\"foo\\u001bbar\"", output.toString());
|
assertEquals("\"foo\\u001bbar\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesControlCharacters() throws IOException {
|
public void escapesControlCharacters() throws IOException {
|
||||||
writer.writeString(string((char) 1, (char) 8, (char) 15, (char) 16, (char) 31));
|
jsonBuilder.buildValue(string((char) 1, (char) 8, (char) 15, (char) 16, (char) 31));
|
||||||
assertEquals("\"\\u0001\\u0008\\u000f\\u0010\\u001f\"", output.toString());
|
assertEquals("\"\\u0001\\u0008\\u000f\\u0010\\u001f\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesFirstChar() throws IOException {
|
public void escapesFirstChar() throws IOException {
|
||||||
writer.writeString(string('\\', 'x'));
|
jsonBuilder.buildValue(string('\\', 'x'));
|
||||||
assertEquals("\"\\\\x\"", output.toString());
|
assertEquals("\"\\\\x\"", output.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapesLastChar() throws IOException {
|
public void escapesLastChar() throws IOException {
|
||||||
writer.writeString(string('x', '\\'));
|
jsonBuilder.buildValue(string('x', '\\'));
|
||||||
assertEquals("\"x\\\\\"", output.toString());
|
assertEquals("\"x\\\\\"", output.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,126 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.xbib.marc.json.JsonLiteral.FALSE;
|
|
||||||
import static org.xbib.marc.json.JsonLiteral.NULL;
|
|
||||||
import static org.xbib.marc.json.JsonLiteral.TRUE;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class JsonLiteralTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isNull() {
|
|
||||||
assertTrue(NULL.isNull());
|
|
||||||
assertFalse(TRUE.isNull());
|
|
||||||
assertFalse(FALSE.isNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isTrue() {
|
|
||||||
assertTrue(TRUE.isTrue());
|
|
||||||
assertFalse(NULL.isTrue());
|
|
||||||
assertFalse(FALSE.isTrue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isFalse() {
|
|
||||||
assertTrue(FALSE.isFalse());
|
|
||||||
assertFalse(NULL.isFalse());
|
|
||||||
assertFalse(TRUE.isFalse());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isBoolean() {
|
|
||||||
assertTrue(TRUE.isBoolean());
|
|
||||||
assertTrue(FALSE.isBoolean());
|
|
||||||
assertFalse(NULL.isBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nullwrite() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
NULL.write(writer);
|
|
||||||
verify(writer).writeLiteral("null");
|
|
||||||
verifyNoMoreInteractions(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void truewrite() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
TRUE.write(writer);
|
|
||||||
verify(writer).writeLiteral("true");
|
|
||||||
verifyNoMoreInteractions(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falsewrite() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
FALSE.write(writer);
|
|
||||||
verify(writer).writeLiteral("false");
|
|
||||||
verifyNoMoreInteractions(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nulltoString() {
|
|
||||||
assertEquals("null", NULL.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void truetoString() {
|
|
||||||
assertEquals("true", TRUE.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falsetoString() {
|
|
||||||
assertEquals("false", FALSE.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nullequals() {
|
|
||||||
assertEquals(NULL, NULL);
|
|
||||||
assertNotEquals(null, NULL);
|
|
||||||
assertNotEquals(NULL, TRUE);
|
|
||||||
assertNotEquals(NULL, FALSE);
|
|
||||||
assertNotEquals(NULL, Json.of("null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void trueequals() {
|
|
||||||
assertEquals(TRUE, TRUE);
|
|
||||||
assertNotEquals(null, TRUE);
|
|
||||||
assertNotEquals(TRUE, FALSE);
|
|
||||||
assertNotEquals(TRUE, Boolean.TRUE);
|
|
||||||
assertNotEquals(NULL, Json.of("true"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falseequals() {
|
|
||||||
assertEquals(FALSE, FALSE);
|
|
||||||
assertNotEquals(null, FALSE);
|
|
||||||
assertNotEquals(FALSE, TRUE);
|
|
||||||
assertNotEquals(FALSE, Boolean.FALSE);
|
|
||||||
assertNotEquals(NULL, Json.of("false"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
public class JsonNumberTest {
|
|
||||||
|
|
||||||
private StringWriter output;
|
|
||||||
|
|
||||||
private JsonWriter writer;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
output = new StringWriter();
|
|
||||||
writer = new JsonWriter(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void constructorfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> new JsonNumber(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void write() throws IOException {
|
|
||||||
new JsonNumber("23").write(writer);
|
|
||||||
assertEquals("23", output.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void toStringreturnsInputString() {
|
|
||||||
assertEquals("foo", new JsonNumber("foo").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isInt() {
|
|
||||||
assertTrue(new JsonNumber("23").isInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asInt() {
|
|
||||||
assertEquals(23, new JsonNumber("23").asInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asIntfailsWithExceedingValues() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("10000000000").asInt();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asIntfailsWithExponent() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("1e5").asInt();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asIntfailsWithFractional() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("23.5").asInt();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asLong() {
|
|
||||||
assertEquals(23L, new JsonNumber("23").asLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asLongfailsWithExceedingValues() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("10000000000000000000").asLong();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asLongfailsWithExponent() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("1e5").asLong();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asLongfailsWithFractional() {
|
|
||||||
Assertions.assertThrows(NumberFormatException.class, () -> {
|
|
||||||
new JsonNumber("23.5").asLong();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asFloat() {
|
|
||||||
assertEquals(23.05f, new JsonNumber("23.05").asFloat(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asFloatreturnsInfinityForExceedingValues() {
|
|
||||||
assertEquals(Float.POSITIVE_INFINITY, new JsonNumber("1e50").asFloat(), 0);
|
|
||||||
assertEquals(Float.NEGATIVE_INFINITY, new JsonNumber("-1e50").asFloat(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asDouble() {
|
|
||||||
double result = new JsonNumber("23.05").asDouble();
|
|
||||||
assertEquals(23.05, result, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asDoublereturnsInfinityForExceedingValues() {
|
|
||||||
assertEquals(Double.POSITIVE_INFINITY, new JsonNumber("1e500").asDouble(), 0);
|
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, new JsonNumber("-1e500").asDouble(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalstrueForSameInstance() {
|
|
||||||
JsonNumber number = new JsonNumber("23");
|
|
||||||
assertEquals(number, number);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalstrueForEqualNumberStrings() {
|
|
||||||
assertEquals(new JsonNumber("23"), new JsonNumber("23"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForDifferentNumberStrings() {
|
|
||||||
assertNotEquals(new JsonNumber("23"), new JsonNumber("42"));
|
|
||||||
assertNotEquals(new JsonNumber("1e+5"), new JsonNumber("1e5"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForNull() {
|
|
||||||
assertNotEquals(null, new JsonNumber("23"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForSubclass() {
|
|
||||||
assertNotEquals(new JsonNumber("23"), new JsonNumber("23") {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodeequalsForEqualStrings() {
|
|
||||||
assertEquals(new JsonNumber("23").hashCode(), new JsonNumber("23").hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodediffersForDifferentStrings() {
|
|
||||||
assertNotEquals(new JsonNumber("23").hashCode(), new JsonNumber("42").hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,925 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.inOrder;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.InOrder;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ConcurrentModificationException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
public class JsonObjectTest {
|
|
||||||
|
|
||||||
private JsonObject object;
|
|
||||||
|
|
||||||
private static JsonObject object(String... namesAndValues) {
|
|
||||||
JsonObject object = new JsonObject();
|
|
||||||
for (int i = 0; i < namesAndValues.length; i += 2) {
|
|
||||||
object.add(namesAndValues[i], namesAndValues[i + 1]);
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
object = new JsonObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> new JsonObject(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorhasSameValues() {
|
|
||||||
object.add("foo", 23);
|
|
||||||
JsonObject copy = new JsonObject(object);
|
|
||||||
assertEquals(object.names(), copy.names());
|
|
||||||
assertSame(object.get("foo"), copy.get("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void copyConstructorworksOnSafeCopy() {
|
|
||||||
JsonObject copy = new JsonObject(object);
|
|
||||||
object.add("foo", 23);
|
|
||||||
assertTrue(copy.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isEmptytrueAfterCreation() {
|
|
||||||
assertTrue(object.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isEmptyfalseAfterAdd() {
|
|
||||||
object.add("a", true);
|
|
||||||
assertFalse(object.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sizezeroAfterCreation() {
|
|
||||||
assertEquals(0, object.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sizeoneAfterAdd() {
|
|
||||||
object.add("a", true);
|
|
||||||
assertEquals(1, object.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void keyRepetitionallowsMultipleEntries() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("a", "value");
|
|
||||||
assertEquals(2, object.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void keyRepetitiongetsLastEntry() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("a", "value");
|
|
||||||
assertEquals("value", object.getString("a", "missing"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void keyRepetitionequalityConsidersRepetitions() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("a", "value");
|
|
||||||
JsonObject onlyFirstProperty = new JsonObject();
|
|
||||||
onlyFirstProperty.add("a", true);
|
|
||||||
assertNotEquals(onlyFirstProperty, object);
|
|
||||||
JsonObject bothProperties = new JsonObject();
|
|
||||||
bothProperties.add("a", true);
|
|
||||||
bothProperties.add("a", "value");
|
|
||||||
assertEquals(bothProperties, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void namesemptyAfterCreation() {
|
|
||||||
assertTrue(object.names().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void namescontainsNameAfterAdd() {
|
|
||||||
object.add("foo", true);
|
|
||||||
List<String> names = object.names();
|
|
||||||
assertEquals(1, names.size());
|
|
||||||
assertEquals("foo", names.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void namesreflectsChanges() {
|
|
||||||
List<String> names = object.names();
|
|
||||||
object.add("foo", true);
|
|
||||||
assertEquals(1, names.size());
|
|
||||||
assertEquals("foo", names.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void namespreventsModification() {
|
|
||||||
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
|
|
||||||
List<String> names = object.names();
|
|
||||||
names.add("foo");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratorisEmptyAfterCreation() {
|
|
||||||
assertFalse(object.iterator().hasNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratorhasNextAfterAdd() {
|
|
||||||
object.add("a", true);
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
assertTrue(iterator.hasNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratornextReturnsActualValue() {
|
|
||||||
object.add("a", true);
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
assertEquals(new JsonObject.Member("a", JsonLiteral.TRUE), iterator.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratornextProgressesToNextValue() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("b", false);
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
iterator.next();
|
|
||||||
assertTrue(iterator.hasNext());
|
|
||||||
assertEquals(new JsonObject.Member("b", JsonLiteral.FALSE), iterator.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratornextFailsAtEnd() {
|
|
||||||
Assertions.assertThrows(NoSuchElementException.class, () -> {
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
iterator.next();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratordoesNotAllowModification() {
|
|
||||||
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
|
|
||||||
object.add("a", 23);
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
iterator.next();
|
|
||||||
iterator.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void iteratordetectsConcurrentModification() {
|
|
||||||
Assertions.assertThrows(ConcurrentModificationException.class, () -> {
|
|
||||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
|
||||||
object.add("a", 23);
|
|
||||||
iterator.next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getfailsWithNullName() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> object.get(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getreturnsNullForNonExistingMember() {
|
|
||||||
assertNull(object.get("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getreturnsValueForName() {
|
|
||||||
object.add("foo", true);
|
|
||||||
assertEquals(JsonLiteral.TRUE, object.get("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getreturnsLastValueForName() {
|
|
||||||
object.add("foo", false).add("foo", true);
|
|
||||||
assertEquals(JsonLiteral.TRUE, object.get("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getintreturnsValueFromMember() {
|
|
||||||
object.add("foo", 23);
|
|
||||||
|
|
||||||
assertEquals(23, object.getInt("foo", 42));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getintreturnsDefaultForMissingMember() {
|
|
||||||
assertEquals(23, object.getInt("foo", 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getlongreturnsValueFromMember() {
|
|
||||||
object.add("foo", 23L);
|
|
||||||
|
|
||||||
assertEquals(23L, object.getLong("foo", 42L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getlongreturnsDefaultForMissingMember() {
|
|
||||||
assertEquals(23L, object.getLong("foo", 23L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getfloatreturnsValueFromMember() {
|
|
||||||
object.add("foo", 3.14f);
|
|
||||||
|
|
||||||
assertEquals(3.14f, object.getFloat("foo", 1.41f), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getfloatreturnsDefaultForMissingMember() {
|
|
||||||
assertEquals(3.14f, object.getFloat("foo", 3.14f), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getdoublereturnsValueFromMember() {
|
|
||||||
object.add("foo", 3.14);
|
|
||||||
|
|
||||||
assertEquals(3.14, object.getDouble("foo", 1.41), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getdoublereturnsDefaultForMissingMember() {
|
|
||||||
assertEquals(3.14, object.getDouble("foo", 3.14), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getbooleanreturnsValueFromMember() {
|
|
||||||
object.add("foo", true);
|
|
||||||
|
|
||||||
assertTrue(object.getBoolean("foo", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getbooleanreturnsDefaultForMissingMember() {
|
|
||||||
assertFalse(object.getBoolean("foo", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getstringreturnsValueFromMember() {
|
|
||||||
object.add("foo", "bar");
|
|
||||||
|
|
||||||
assertEquals("bar", object.getString("foo", "default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getstringreturnsDefaultForMissingMember() {
|
|
||||||
assertEquals("default", object.getString("foo", "default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addfailsWithNullName() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> object.add(null, 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addint() {
|
|
||||||
object.add("a", 23);
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addintenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addlong() {
|
|
||||||
object.add("a", 23L);
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addlongenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", 23L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addfloat() {
|
|
||||||
object.add("a", 3.14f);
|
|
||||||
assertEquals("{\"a\":3.14}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addfloatenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", 3.14f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void adddouble() {
|
|
||||||
object.add("a", 3.14d);
|
|
||||||
assertEquals("{\"a\":3.14}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void adddoubleenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", 3.14d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addboolean() {
|
|
||||||
object.add("a", true);
|
|
||||||
assertEquals("{\"a\":true}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addbooleanenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstring() {
|
|
||||||
object.add("a", "foo");
|
|
||||||
assertEquals("{\"a\":\"foo\"}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstringtoleratesNull() {
|
|
||||||
object.add("a", (String) null);
|
|
||||||
assertEquals("{\"a\":null}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addstringenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", "foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonNull() {
|
|
||||||
object.add("a", JsonLiteral.NULL);
|
|
||||||
assertEquals("{\"a\":null}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonArray() {
|
|
||||||
object.add("a", new JsonArray());
|
|
||||||
assertEquals("{\"a\":[]}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonObject() {
|
|
||||||
object.add("a", new JsonObject());
|
|
||||||
assertEquals("{\"a\":{}}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonenablesChaining() {
|
|
||||||
assertSame(object, object.add("a", JsonLiteral.NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () ->
|
|
||||||
object.add("a", (JsonValue) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedArray() {
|
|
||||||
JsonArray innerArray = new JsonArray();
|
|
||||||
innerArray.add(23);
|
|
||||||
object.add("a", innerArray);
|
|
||||||
assertEquals("{\"a\":[23]}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedArraymodifiedAfterAdd() {
|
|
||||||
JsonArray innerArray = new JsonArray();
|
|
||||||
object.add("a", innerArray);
|
|
||||||
innerArray.add(23);
|
|
||||||
assertEquals("{\"a\":[23]}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedObject() {
|
|
||||||
JsonObject innerObject = new JsonObject();
|
|
||||||
innerObject.add("a", 23);
|
|
||||||
object.add("a", innerObject);
|
|
||||||
assertEquals("{\"a\":{\"a\":23}}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addjsonnestedObjectmodifiedAfterAdd() {
|
|
||||||
JsonObject innerObject = new JsonObject();
|
|
||||||
object.add("a", innerObject);
|
|
||||||
innerObject.add("a", 23);
|
|
||||||
assertEquals("{\"a\":{\"a\":23}}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setint() {
|
|
||||||
object.set("a", 23);
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setintenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setlong() {
|
|
||||||
object.set("a", 23L);
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setlongenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", 23L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setfloat() {
|
|
||||||
object.set("a", 3.14f);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":3.14}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setfloatenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", 3.14f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setdouble() {
|
|
||||||
object.set("a", 3.14d);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":3.14}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setdoubleenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", 3.14d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setboolean() {
|
|
||||||
object.set("a", true);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":true}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setbooleanenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setstring() {
|
|
||||||
object.set("a", "foo");
|
|
||||||
|
|
||||||
assertEquals("{\"a\":\"foo\"}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setstringenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", "foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonNull() {
|
|
||||||
object.set("a", JsonLiteral.NULL);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":null}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonArray() {
|
|
||||||
object.set("a", new JsonArray());
|
|
||||||
|
|
||||||
assertEquals("{\"a\":[]}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonObject() {
|
|
||||||
object.set("a", new JsonObject());
|
|
||||||
|
|
||||||
assertEquals("{\"a\":{}}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setjsonenablesChaining() {
|
|
||||||
assertSame(object, object.set("a", JsonLiteral.NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setaddsElementIfMissing() {
|
|
||||||
object.set("a", JsonLiteral.TRUE);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":true}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setmodifiesElementIfExisting() {
|
|
||||||
object.add("a", JsonLiteral.TRUE);
|
|
||||||
|
|
||||||
object.set("a", JsonLiteral.FALSE);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":false}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setmodifiesLastElementIfMultipleExisting() {
|
|
||||||
object.add("a", 1);
|
|
||||||
object.add("a", 2);
|
|
||||||
|
|
||||||
object.set("a", JsonLiteral.TRUE);
|
|
||||||
|
|
||||||
assertEquals("{\"a\":1,\"a\":true}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removefailsWithNullName() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> object.remove(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeremovesMatchingMember() {
|
|
||||||
object.add("a", 23);
|
|
||||||
|
|
||||||
object.remove("a");
|
|
||||||
|
|
||||||
assertEquals("{}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeremovesOnlyMatchingMember() {
|
|
||||||
object.add("a", 23);
|
|
||||||
object.add("b", 42);
|
|
||||||
object.add("c", true);
|
|
||||||
|
|
||||||
object.remove("b");
|
|
||||||
|
|
||||||
assertEquals("{\"a\":23,\"c\":true}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeremovesOnlyLastMatchingMember() {
|
|
||||||
object.add("a", 23);
|
|
||||||
object.add("a", 42);
|
|
||||||
|
|
||||||
object.remove("a");
|
|
||||||
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeremovesOnlyLastMatchingMemberafterRemove() {
|
|
||||||
object.add("a", 23);
|
|
||||||
object.remove("a");
|
|
||||||
object.add("a", 42);
|
|
||||||
object.add("a", 47);
|
|
||||||
|
|
||||||
object.remove("a");
|
|
||||||
|
|
||||||
assertEquals("{\"a\":42}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removedoesNotModifyObjectWithoutMatchingMember() {
|
|
||||||
object.add("a", 23);
|
|
||||||
|
|
||||||
object.remove("b");
|
|
||||||
|
|
||||||
assertEquals("{\"a\":23}", object.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergefailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> object.merge(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergeappendsMembers() {
|
|
||||||
object.add("a", 1).add("b", 1);
|
|
||||||
object.merge(Json.object().add("c", 2).add("d", 2));
|
|
||||||
|
|
||||||
assertEquals(Json.object().add("a", 1).add("b", 1).add("c", 2).add("d", 2), object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void mergereplacesMembers() {
|
|
||||||
object.add("a", 1).add("b", 1).add("c", 1);
|
|
||||||
object.merge(Json.object().add("b", 2).add("d", 2));
|
|
||||||
|
|
||||||
assertEquals(Json.object().add("a", 1).add("b", 2).add("c", 1).add("d", 2), object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeempty() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
object.write(writer);
|
|
||||||
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeObjectOpen();
|
|
||||||
inOrder.verify(writer).writeObjectClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writewithSingleValue() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
object.add("a", 23);
|
|
||||||
|
|
||||||
object.write(writer);
|
|
||||||
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeObjectOpen();
|
|
||||||
inOrder.verify(writer).writeMemberName("a");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeNumber("23");
|
|
||||||
inOrder.verify(writer).writeObjectClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writewithMultipleValues() throws IOException {
|
|
||||||
JsonWriter writer = mock(JsonWriter.class);
|
|
||||||
object.add("a", 23);
|
|
||||||
object.add("b", 3.14f);
|
|
||||||
object.add("c", "foo");
|
|
||||||
object.add("d", true);
|
|
||||||
object.add("e", (String) null);
|
|
||||||
|
|
||||||
object.write(writer);
|
|
||||||
|
|
||||||
InOrder inOrder = inOrder(writer);
|
|
||||||
inOrder.verify(writer).writeObjectOpen();
|
|
||||||
inOrder.verify(writer).writeMemberName("a");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeNumber("23");
|
|
||||||
inOrder.verify(writer).writeObjectSeparator();
|
|
||||||
inOrder.verify(writer).writeMemberName("b");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeNumber("3.14");
|
|
||||||
inOrder.verify(writer).writeObjectSeparator();
|
|
||||||
inOrder.verify(writer).writeMemberName("c");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeString("foo");
|
|
||||||
inOrder.verify(writer).writeObjectSeparator();
|
|
||||||
inOrder.verify(writer).writeMemberName("d");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeLiteral("true");
|
|
||||||
inOrder.verify(writer).writeObjectSeparator();
|
|
||||||
inOrder.verify(writer).writeMemberName("e");
|
|
||||||
inOrder.verify(writer).writeMemberSeparator();
|
|
||||||
inOrder.verify(writer).writeLiteral("null");
|
|
||||||
inOrder.verify(writer).writeObjectClose();
|
|
||||||
inOrder.verifyNoMoreInteractions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isObject() {
|
|
||||||
assertTrue(object.isObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asObject() {
|
|
||||||
assertSame(object, object.asObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalstrueForSameInstance() {
|
|
||||||
assertEquals(object, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalstrueForEqualObjects() {
|
|
||||||
assertEquals(object(), object());
|
|
||||||
assertEquals(object("a", "1", "b", "2"), object("a", "1", "b", "2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForDifferentObjects() {
|
|
||||||
assertNotEquals(object("a", "1"), object("a", "2"));
|
|
||||||
assertNotEquals(object("a", "1"), object("b", "1"));
|
|
||||||
assertNotEquals(object("a", "1", "b", "2"), object("b", "2", "a", "1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForNull() {
|
|
||||||
assertNotEquals(null, new JsonObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsfalseForSubclass() {
|
|
||||||
JsonObject jsonObject = new JsonObject();
|
|
||||||
|
|
||||||
assertNotEquals(jsonObject, new JsonObject(jsonObject) {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodeequalsForEqualObjects() {
|
|
||||||
assertEquals(object().hashCode(), object().hashCode());
|
|
||||||
assertEquals(object("a", "1").hashCode(), object("a", "1").hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodediffersForDifferentObjects() {
|
|
||||||
assertNotEquals(object().hashCode(), object("a", "1").hashCode());
|
|
||||||
assertNotEquals(object("a", "1").hashCode(), object("a", "2").hashCode());
|
|
||||||
assertNotEquals(object("a", "1").hashCode(), object("b", "1").hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsNoIndexIfEmpty() {
|
|
||||||
assertEquals(-1, object.indexOf("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsIndexOfMember() {
|
|
||||||
object.add("a", true);
|
|
||||||
|
|
||||||
assertEquals(0, object.indexOf("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsIndexOfLastMember() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("a", true);
|
|
||||||
|
|
||||||
assertEquals(1, object.indexOf("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsIndexOfLastMemberafterRemove() {
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("a", true);
|
|
||||||
object.remove("a");
|
|
||||||
|
|
||||||
assertEquals(0, object.indexOf("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsUpdatedIndexAfterRemove() {
|
|
||||||
// See issue #16
|
|
||||||
object.add("a", true);
|
|
||||||
object.add("b", true);
|
|
||||||
object.remove("a");
|
|
||||||
|
|
||||||
assertEquals(0, object.indexOf("b"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void indexOfreturnsIndexOfLastMemberforBigObject() {
|
|
||||||
object.add("a", true);
|
|
||||||
// for indexes above 255, the hash index table does not return a value
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
object.add("x-" + i, 0);
|
|
||||||
}
|
|
||||||
object.add("a", true);
|
|
||||||
|
|
||||||
assertEquals(257, object.indexOf("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTablecopyConstructor() {
|
|
||||||
JsonObject.HashIndexTable original = new JsonObject.HashIndexTable();
|
|
||||||
original.add("name", 23);
|
|
||||||
JsonObject.HashIndexTable copy = new JsonObject.HashIndexTable(original);
|
|
||||||
assertEquals(23, copy.get("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableadd() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
|
|
||||||
indexTable.add("name-0", 0);
|
|
||||||
indexTable.add("name-1", 1);
|
|
||||||
indexTable.add("name-fe", 0xfe);
|
|
||||||
indexTable.add("name-ff", 0xff);
|
|
||||||
|
|
||||||
assertEquals(0, indexTable.get("name-0"));
|
|
||||||
assertEquals(1, indexTable.get("name-1"));
|
|
||||||
assertEquals(0xfe, indexTable.get("name-fe"));
|
|
||||||
assertEquals(-1, indexTable.get("name-ff"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableaddoverwritesPreviousValue() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
indexTable.add("name", 23);
|
|
||||||
indexTable.add("name", 42);
|
|
||||||
assertEquals(42, indexTable.get("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableaddclearsPreviousValueIfIndexExceeds0xff() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
indexTable.add("name", 23);
|
|
||||||
indexTable.add("name", 300);
|
|
||||||
assertEquals(-1, indexTable.get("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableremove() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
indexTable.add("name", 23);
|
|
||||||
indexTable.remove(23);
|
|
||||||
assertEquals(-1, indexTable.get("name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableremoveupdatesSubsequentElements() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
indexTable.add("foo", 23);
|
|
||||||
indexTable.add("bar", 42);
|
|
||||||
indexTable.remove(23);
|
|
||||||
assertEquals(41, indexTable.get("bar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashIndexTableremovedoesNotChangePrecedingElements() {
|
|
||||||
JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable();
|
|
||||||
indexTable.add("foo", 23);
|
|
||||||
indexTable.add("bar", 42);
|
|
||||||
indexTable.remove(42);
|
|
||||||
assertEquals(23, indexTable.get("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberreturnsNameAndValue() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertEquals("a", member.getName());
|
|
||||||
assertEquals(JsonLiteral.TRUE, member.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberequalstrueForSameInstance() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertEquals(member, member);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberequalstrueForEqualObjects() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertEquals(member, new JsonObject.Member("a", JsonLiteral.TRUE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberequalsfalseForDifferingObjects() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertNotEquals(member, new JsonObject.Member("b", JsonLiteral.TRUE));
|
|
||||||
assertNotEquals(member, new JsonObject.Member("a", JsonLiteral.FALSE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberequalsfalseForNull() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertNotNull(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberequalsfalseForSubclass() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertNotEquals(member, new JsonObject.Member("a", JsonLiteral.TRUE) {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberhashCodeequalsForEqualObjects() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertEquals(member.hashCode(), new JsonObject.Member("a", JsonLiteral.TRUE).hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void memberhashCodediffersForDifferingobjects() {
|
|
||||||
JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE);
|
|
||||||
assertNotEquals(member.hashCode(), new JsonObject.Member("b", JsonLiteral.TRUE).hashCode());
|
|
||||||
assertNotEquals(member.hashCode(), new JsonObject.Member("a", JsonLiteral.FALSE).hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,812 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.core.StringStartsWith.startsWith;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.xbib.marc.json.Json.parse;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
|
|
||||||
public class JsonReaderTest {
|
|
||||||
|
|
||||||
private static String join(String... strings) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (String string : strings) {
|
|
||||||
builder.append(string).append('\n');
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void constructorRejectsNullHandler() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> {
|
|
||||||
new JsonReader<>(null, null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseStringrRejectsNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> {
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(null), new TestHandler());
|
|
||||||
reader.parse();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseReaderRejectsNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> {
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(null, new TestHandler());
|
|
||||||
reader.parse();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseReaderRejectsNegativeBufferSize() {
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> {
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("[]"), new TestHandler());
|
|
||||||
reader.parse(-1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseStringRejectsEmpty() {
|
|
||||||
assertParseException(0, "Unexpected end of input", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseReaderRejectsEmpty() {
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(""), new TestHandler());
|
|
||||||
JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse);
|
|
||||||
assertThat(exception.getMessage(), startsWith("Unexpected end of input"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseNull() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("null"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startNull ",
|
|
||||||
"endNull "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseTrue() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("true"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startBoolean ",
|
|
||||||
"endBoolean true "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseFalse() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("false"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startBoolean ",
|
|
||||||
"endBoolean false "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseString() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("\"foo\""), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startString ",
|
|
||||||
"endString foo "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseStringEmpty() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("\"\""), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startString ",
|
|
||||||
"endString "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseNumber() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("23"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startNumber ",
|
|
||||||
"endNumber 23 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseNumberNegative() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("-23"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startNumber ",
|
|
||||||
"endNumber -23 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsenumbernegativeexponent() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("-2.3e-12"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startNumber ",
|
|
||||||
"endNumber -2.3e-12 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsearray() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("[23]"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startArray ",
|
|
||||||
"startArrayValue a1 ",
|
|
||||||
"startNumber ",
|
|
||||||
"endNumber 23 ",
|
|
||||||
"endArrayValue a1 ",
|
|
||||||
"endArray a1 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsearrayempty() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("[]"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startArray ",
|
|
||||||
"endArray a1 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseobject() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("{\"foo\": 23}"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startObject ",
|
|
||||||
"startObjectName o1 ",
|
|
||||||
"endObjectName o1 foo ",
|
|
||||||
"startObjectValue o1 foo ",
|
|
||||||
"startNumber ",
|
|
||||||
"endNumber 23 ",
|
|
||||||
"endObjectValue o1 foo ",
|
|
||||||
"endObject o1 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseobjectempty() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("{}"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join("startObject ",
|
|
||||||
"endObject o1 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsestripsPadding() throws IOException {
|
|
||||||
assertEquals(new JsonArray(), parse(" [ ] "));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseignoresAllWhiteSpace() throws IOException {
|
|
||||||
assertEquals(new JsonArray(), parse("\t\r\n [\t\r\n ]\t\r\n "));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsefailsWithUnterminatedString() {
|
|
||||||
assertParseException(5, "Unexpected end of input", "[\"foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsehandlesInputsThatExceedBufferSize() throws IOException {
|
|
||||||
String input = "[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 ]";
|
|
||||||
JsonDefaultHandler defHandler = new JsonDefaultHandler();
|
|
||||||
JsonReader<JsonArray, JsonObject> reader = new JsonReader<>(new StringReader(input), defHandler);
|
|
||||||
reader.parse(3);
|
|
||||||
assertEquals("[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]", defHandler.getValue().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsehandlesStringsThatExceedBufferSize() throws IOException {
|
|
||||||
String input = "[ \"lorem ipsum dolor sit amet\" ]";
|
|
||||||
JsonDefaultHandler defHandler = new JsonDefaultHandler();
|
|
||||||
JsonReader<JsonArray, JsonObject> reader = new JsonReader<>(new StringReader(input), defHandler);
|
|
||||||
reader.parse(3);
|
|
||||||
assertEquals("[\"lorem ipsum dolor sit amet\"]", defHandler.getValue().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsehandlesNumbersThatExceedBufferSize() throws IOException {
|
|
||||||
String input = "[ 3.141592653589 ]";
|
|
||||||
JsonDefaultHandler defHandler = new JsonDefaultHandler();
|
|
||||||
JsonReader<JsonArray, JsonObject> reader = new JsonReader<>(new StringReader(input), defHandler);
|
|
||||||
reader.parse(3);
|
|
||||||
assertEquals("[3.141592653589]", defHandler.getValue().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsehandlesPositionsCorrectlyWhenInputExceedsBufferSize() {
|
|
||||||
final String input = "{\n \"a\": 23,\n \"b\": 42,\n}";
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(input), handler);
|
|
||||||
Assertions.assertThrows(JsonException.class, () -> reader.parse(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsefailsOnTooDeeplyNestedArray() {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
array = new JsonArray().add(array);
|
|
||||||
}
|
|
||||||
final String input = array.toString();
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(input), handler);
|
|
||||||
JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse);
|
|
||||||
assertEquals("Nesting too deep", exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsefailsOnTooDeeplyNestedObject() {
|
|
||||||
JsonObject object = new JsonObject();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
object = new JsonObject().add("foo", object);
|
|
||||||
}
|
|
||||||
final String input = object.toString();
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(input), handler);
|
|
||||||
JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse);
|
|
||||||
assertEquals("Nesting too deep", exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsefailsOnTooDeeplyNestedMixedObject() {
|
|
||||||
JsonValue value = new JsonObject();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
value = i % 2 == 0 ? new JsonArray().add(value) : new JsonObject().add("foo", value);
|
|
||||||
}
|
|
||||||
final String input = value.toString();
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(input), handler);
|
|
||||||
JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse);
|
|
||||||
assertEquals("Nesting too deep", exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsedoesNotFailWithManyArrays() throws IOException {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
array.add(new JsonArray().add(7));
|
|
||||||
}
|
|
||||||
final String input = array.toString();
|
|
||||||
JsonValue result = parse(input);
|
|
||||||
assertTrue(result.isArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsedoesNotFailWithManyEmptyArrays() throws IOException {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
array.add(new JsonArray());
|
|
||||||
}
|
|
||||||
final String input = array.toString();
|
|
||||||
JsonValue result = parse(input);
|
|
||||||
assertTrue(result.isArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsedoesNotFailWithManyObjects() throws IOException {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
array.add(new JsonObject().add("a", 7));
|
|
||||||
}
|
|
||||||
final String input = array.toString();
|
|
||||||
JsonValue result = parse(input);
|
|
||||||
assertTrue(result.isArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsedoesNotFailWithManyEmptyObjects() throws IOException {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (int i = 0; i < 1001; i++) {
|
|
||||||
array.add(new JsonObject());
|
|
||||||
}
|
|
||||||
final String input = array.toString();
|
|
||||||
JsonValue result = parse(input);
|
|
||||||
assertTrue(result.isArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseCanBeCalledOnce() throws IOException {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader("[23]"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join(
|
|
||||||
"startArray ",
|
|
||||||
"startArrayValue a1 ",
|
|
||||||
"startNumber ",
|
|
||||||
"endNumber 23 ",
|
|
||||||
"endArrayValue a1 ",
|
|
||||||
"endArray a1 "),
|
|
||||||
handler.getLog());
|
|
||||||
handler = new TestHandler();
|
|
||||||
reader = new JsonReader<>(new StringReader("[42]"), handler);
|
|
||||||
reader.parse();
|
|
||||||
assertEquals(join(
|
|
||||||
"startArray ",
|
|
||||||
"startArrayValue a1 ",
|
|
||||||
"startNumber ",
|
|
||||||
"endNumber 42 ",
|
|
||||||
"endArrayValue a1 ",
|
|
||||||
"endArray a1 "),
|
|
||||||
handler.getLog());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraysempty() throws IOException {
|
|
||||||
assertEquals("[]", parse("[]").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayssingleValue() throws IOException {
|
|
||||||
assertEquals("[23]", parse("[23]").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraysmultipleValues() throws IOException {
|
|
||||||
assertEquals("[23,42]", parse("[23,42]").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayswithWhitespaces() throws IOException {
|
|
||||||
assertEquals("[23,42]", parse("[ 23 , 42 ]").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraysnested() throws IOException {
|
|
||||||
assertEquals("[[23]]", parse("[[23]]").toString());
|
|
||||||
assertEquals("[[[]]]", parse("[[[]]]").toString());
|
|
||||||
assertEquals("[[23],42]", parse("[[23],42]").toString());
|
|
||||||
assertEquals("[[23],[42]]", parse("[[23],[42]]").toString());
|
|
||||||
assertEquals("[[23],[42]]", parse("[[23],[42]]").toString());
|
|
||||||
assertEquals("[{\"foo\":[23]},{\"bar\":[42]}]",
|
|
||||||
parse("[{\"foo\":[23]},{\"bar\":[42]}]").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraysillegalSyntax() {
|
|
||||||
assertParseException(1, "Expected value", "[,]");
|
|
||||||
assertParseException(4, "Expected ',' or ']'", "[23 42]");
|
|
||||||
assertParseException(4, "Expected value", "[23,]");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraysincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "[");
|
|
||||||
assertParseException(2, "Unexpected end of input", "[ ");
|
|
||||||
assertParseException(3, "Unexpected end of input", "[23");
|
|
||||||
assertParseException(4, "Unexpected end of input", "[23 ");
|
|
||||||
assertParseException(4, "Unexpected end of input", "[23,");
|
|
||||||
assertParseException(5, "Unexpected end of input", "[23, ");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectsempty() throws IOException {
|
|
||||||
assertEquals("{}", parse("{}").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectssingleValue() throws IOException {
|
|
||||||
assertEquals("{\"foo\":23}", parse("{\"foo\":23}").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectsmultipleValues() throws IOException {
|
|
||||||
assertEquals("{\"foo\":23,\"bar\":42}", parse("{\"foo\":23,\"bar\":42}").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectswhitespace() throws IOException {
|
|
||||||
assertEquals("{\"foo\":23,\"bar\":42}", parse("{ \"foo\" : 23, \"bar\" : 42 }").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectsnested() throws IOException {
|
|
||||||
assertEquals("{\"foo\":{}}", parse("{\"foo\":{}}").toString());
|
|
||||||
assertEquals("{\"foo\":{\"bar\":42}}", parse("{\"foo\":{\"bar\": 42}}").toString());
|
|
||||||
assertEquals("{\"foo\":{\"bar\":{\"baz\":42}}}",
|
|
||||||
parse("{\"foo\":{\"bar\": {\"baz\": 42}}}").toString());
|
|
||||||
assertEquals("{\"foo\":[{\"bar\":{\"baz\":[[42]]}}]}",
|
|
||||||
parse("{\"foo\":[{\"bar\": {\"baz\": [[42]]}}]}").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectsillegalSyntax() {
|
|
||||||
assertParseException(1, "Expected name", "{,}");
|
|
||||||
assertParseException(1, "Expected name", "{:}");
|
|
||||||
assertParseException(1, "Expected name", "{23}");
|
|
||||||
assertParseException(4, "Expected ':'", "{\"a\"}");
|
|
||||||
assertParseException(5, "Expected ':'", "{\"a\" \"b\"}");
|
|
||||||
assertParseException(5, "Expected value", "{\"a\":}");
|
|
||||||
assertParseException(8, "Expected name", "{\"a\":23,}");
|
|
||||||
assertParseException(8, "Expected name", "{\"a\":23,42");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void objectsincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "{");
|
|
||||||
assertParseException(2, "Unexpected end of input", "{ ");
|
|
||||||
assertParseException(2, "Unexpected end of input", "{\"");
|
|
||||||
assertParseException(4, "Unexpected end of input", "{\"a\"");
|
|
||||||
assertParseException(5, "Unexpected end of input", "{\"a\" ");
|
|
||||||
assertParseException(5, "Unexpected end of input", "{\"a\":");
|
|
||||||
assertParseException(6, "Unexpected end of input", "{\"a\": ");
|
|
||||||
assertParseException(7, "Unexpected end of input", "{\"a\":23");
|
|
||||||
assertParseException(8, "Unexpected end of input", "{\"a\":23 ");
|
|
||||||
assertParseException(8, "Unexpected end of input", "{\"a\":23,");
|
|
||||||
assertParseException(9, "Unexpected end of input", "{\"a\":23, ");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsemptyStringisAccepted() throws IOException {
|
|
||||||
assertEquals("", parse("\"\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsasciiCharactersareAccepted() throws IOException {
|
|
||||||
assertEquals(" ", parse("\" \"").asString());
|
|
||||||
assertEquals("a", parse("\"a\"").asString());
|
|
||||||
assertEquals("foo", parse("\"foo\"").asString());
|
|
||||||
assertEquals("A2-D2", parse("\"A2-D2\"").asString());
|
|
||||||
assertEquals("\u007f", parse("\"\u007f\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsnonAsciiCharactersareAccepted() throws IOException {
|
|
||||||
assertEquals("Русский", parse("\"Русский\"").asString());
|
|
||||||
assertEquals("العربية", parse("\"العربية\"").asString());
|
|
||||||
assertEquals("日本語", parse("\"日本語\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringscontrolCharactersareRejected() {
|
|
||||||
// JSON string must not contain characters < 0x20
|
|
||||||
assertParseException(3, "Expected valid string character", "\"--\n--\"");
|
|
||||||
assertParseException(3, "Expected valid string character", "\"--\r\n--\"");
|
|
||||||
assertParseException(3, "Expected valid string character", "\"--\t--\"");
|
|
||||||
assertParseException(3, "Expected valid string character", "\"--\u0000--\"");
|
|
||||||
assertParseException(3, "Expected valid string character", "\"--\u001f--\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsvalidEscapesareAccepted() throws IOException {
|
|
||||||
// valid escapes are \" \\ \/ \b \f \n \r \t and unicode escapes
|
|
||||||
assertEquals(" \" ", parse("\" \\\" \"").asString());
|
|
||||||
assertEquals(" \\ ", parse("\" \\\\ \"").asString());
|
|
||||||
assertEquals(" / ", parse("\" \\/ \"").asString());
|
|
||||||
assertEquals(" \u0008 ", parse("\" \\b \"").asString());
|
|
||||||
assertEquals(" \u000c ", parse("\" \\f \"").asString());
|
|
||||||
assertEquals(" \r ", parse("\" \\r \"").asString());
|
|
||||||
assertEquals(" \n ", parse("\" \\n \"").asString());
|
|
||||||
assertEquals(" \t ", parse("\" \\t \"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsescapeatStart() throws IOException {
|
|
||||||
assertEquals("\\x", parse("\"\\\\x\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsescapeatEnd() throws IOException {
|
|
||||||
assertEquals("x\\", parse("\"x\\\\\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsillegalEscapesareRejected() {
|
|
||||||
assertParseException(2, "Expected valid escape sequence", "\"\\a\"");
|
|
||||||
assertParseException(2, "Expected valid escape sequence", "\"\\x\"");
|
|
||||||
assertParseException(2, "Expected valid escape sequence", "\"\\000\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsvalidUnicodeEscapesareAccepted() throws IOException {
|
|
||||||
assertEquals("\u0021", parse("\"\\u0021\"").asString());
|
|
||||||
assertEquals("\u4711", parse("\"\\u4711\"").asString());
|
|
||||||
assertEquals("\uffff", parse("\"\\uffff\"").asString());
|
|
||||||
assertEquals("\uabcdx", parse("\"\\uabcdx\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsillegalUnicodeEscapesareRejected() {
|
|
||||||
assertParseException(3, "Expected hexadecimal digit", "\"\\u \"");
|
|
||||||
assertParseException(3, "Expected hexadecimal digit", "\"\\ux\"");
|
|
||||||
assertParseException(5, "Expected hexadecimal digit", "\"\\u20 \"");
|
|
||||||
assertParseException(6, "Expected hexadecimal digit", "\"\\u000x\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void stringsincompleteStringsareRejected() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "\"");
|
|
||||||
assertParseException(4, "Unexpected end of input", "\"foo");
|
|
||||||
assertParseException(5, "Unexpected end of input", "\"foo\\");
|
|
||||||
assertParseException(6, "Unexpected end of input", "\"foo\\n");
|
|
||||||
assertParseException(6, "Unexpected end of input", "\"foo\\u");
|
|
||||||
assertParseException(7, "Unexpected end of input", "\"foo\\u0");
|
|
||||||
assertParseException(9, "Unexpected end of input", "\"foo\\u000");
|
|
||||||
assertParseException(10, "Unexpected end of input", "\"foo\\u0000");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numbersinteger() throws IOException {
|
|
||||||
assertEquals(new JsonNumber("0"), parse("0"));
|
|
||||||
assertEquals(new JsonNumber("-0"), parse("-0"));
|
|
||||||
assertEquals(new JsonNumber("1"), parse("1"));
|
|
||||||
assertEquals(new JsonNumber("-1"), parse("-1"));
|
|
||||||
assertEquals(new JsonNumber("23"), parse("23"));
|
|
||||||
assertEquals(new JsonNumber("-23"), parse("-23"));
|
|
||||||
assertEquals(new JsonNumber("1234567890"), parse("1234567890"));
|
|
||||||
assertEquals(new JsonNumber("123456789012345678901234567890"),
|
|
||||||
parse("123456789012345678901234567890"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numbersminusZero() throws IOException {
|
|
||||||
// allowed by JSON, allowed by Java
|
|
||||||
JsonValue value = parse("-0");
|
|
||||||
assertEquals(0, value.asInt());
|
|
||||||
assertEquals(0L, value.asLong());
|
|
||||||
assertEquals(0f, value.asFloat(), 0);
|
|
||||||
assertEquals(0d, value.asDouble(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numbersdecimal() throws IOException {
|
|
||||||
assertEquals(new JsonNumber("0.23"), parse("0.23"));
|
|
||||||
assertEquals(new JsonNumber("-0.23"), parse("-0.23"));
|
|
||||||
assertEquals(new JsonNumber("1234567890.12345678901234567890"),
|
|
||||||
parse("1234567890.12345678901234567890"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numberswithExponent() throws IOException {
|
|
||||||
assertEquals(new JsonNumber("0.1e9"), parse("0.1e9"));
|
|
||||||
assertEquals(new JsonNumber("0.1e9"), parse("0.1e9"));
|
|
||||||
assertEquals(new JsonNumber("0.1E9"), parse("0.1E9"));
|
|
||||||
assertEquals(new JsonNumber("-0.23e9"), parse("-0.23e9"));
|
|
||||||
assertEquals(new JsonNumber("0.23e9"), parse("0.23e9"));
|
|
||||||
assertEquals(new JsonNumber("0.23e+9"), parse("0.23e+9"));
|
|
||||||
assertEquals(new JsonNumber("0.23e-9"), parse("0.23e-9"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numberswithInvalidFormat() {
|
|
||||||
assertParseException(0, "Expected value", "+1");
|
|
||||||
assertParseException(0, "Expected value", ".1");
|
|
||||||
assertParseException(1, "Unexpected character", "02");
|
|
||||||
assertParseException(2, "Unexpected character", "-02");
|
|
||||||
assertParseException(1, "Expected digit", "-x");
|
|
||||||
assertParseException(2, "Expected digit", "1.x");
|
|
||||||
assertParseException(2, "Expected digit", "1ex");
|
|
||||||
assertParseException(3, "Unexpected character", "1e1x");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void numbersincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "-");
|
|
||||||
assertParseException(2, "Unexpected end of input", "1.");
|
|
||||||
assertParseException(4, "Unexpected end of input", "1.0e");
|
|
||||||
assertParseException(5, "Unexpected end of input", "1.0e-");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nullcomplete() throws IOException {
|
|
||||||
assertEquals(JsonLiteral.NULL, parse("null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nullincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "n");
|
|
||||||
assertParseException(2, "Unexpected end of input", "nu");
|
|
||||||
assertParseException(3, "Unexpected end of input", "nul");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void nullwithIllegalCharacter() {
|
|
||||||
assertParseException(1, "Expected 'u'", "nx");
|
|
||||||
assertParseException(2, "Expected 'l'", "nux");
|
|
||||||
assertParseException(3, "Expected 'l'", "nulx");
|
|
||||||
assertParseException(4, "Unexpected character", "nullx");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void truecomplete() throws IOException {
|
|
||||||
assertSame(JsonLiteral.TRUE, parse("true"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void trueincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "t");
|
|
||||||
assertParseException(2, "Unexpected end of input", "tr");
|
|
||||||
assertParseException(3, "Unexpected end of input", "tru");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void truewithIllegalCharacter() {
|
|
||||||
assertParseException(1, "Expected 'r'", "tx");
|
|
||||||
assertParseException(2, "Expected 'u'", "trx");
|
|
||||||
assertParseException(3, "Expected 'e'", "trux");
|
|
||||||
assertParseException(4, "Unexpected character", "truex");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falsecomplete() throws IOException {
|
|
||||||
assertSame(JsonLiteral.FALSE, parse("false"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falseincomplete() {
|
|
||||||
assertParseException(1, "Unexpected end of input", "f");
|
|
||||||
assertParseException(2, "Unexpected end of input", "fa");
|
|
||||||
assertParseException(3, "Unexpected end of input", "fal");
|
|
||||||
assertParseException(4, "Unexpected end of input", "fals");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void falsewithIllegalCharacter() {
|
|
||||||
assertParseException(1, "Expected 'a'", "fx");
|
|
||||||
assertParseException(2, "Expected 'l'", "fax");
|
|
||||||
assertParseException(3, "Expected 's'", "falx");
|
|
||||||
assertParseException(4, "Expected 'e'", "falsx");
|
|
||||||
assertParseException(5, "Unexpected character", "falsex");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertParseException(int offset, String message, final String json) {
|
|
||||||
TestHandler handler = new TestHandler();
|
|
||||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(json), handler);
|
|
||||||
JsonException exception = Assertions.assertThrows(JsonException.class, () -> {
|
|
||||||
try {
|
|
||||||
reader.parse();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertThat(exception.getMessage(), startsWith(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestHandler implements JsonHandler<Object, Object> {
|
|
||||||
|
|
||||||
StringBuilder log = new StringBuilder();
|
|
||||||
int sequence = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startNull() {
|
|
||||||
record("startNull");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endNull() {
|
|
||||||
record("endNull");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startBoolean() {
|
|
||||||
record("startBoolean");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endBoolean(boolean value) {
|
|
||||||
record("endBoolean", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startString() {
|
|
||||||
record("startString");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endString(String string) {
|
|
||||||
record("endString", string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startNumber() {
|
|
||||||
record("startNumber");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endNumber(String string) {
|
|
||||||
record("endNumber", string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object startArray() {
|
|
||||||
record("startArray");
|
|
||||||
return "a" + ++sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endArray(Object array) {
|
|
||||||
record("endArray", array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startArrayValue(Object array) {
|
|
||||||
record("startArrayValue", array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endArrayValue(Object array) {
|
|
||||||
record("endArrayValue", array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object startObject() {
|
|
||||||
record("startObject");
|
|
||||||
return "o" + ++sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObject(Object object) {
|
|
||||||
record("endObject", object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startObjectName(Object object) {
|
|
||||||
record("startObjectName", object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObjectName(Object object, String name) {
|
|
||||||
record("endObjectName", object, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startObjectValue(Object object, String name) {
|
|
||||||
record("startObjectValue", object, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endObjectValue(Object object, String name) {
|
|
||||||
record("endObjectValue", object, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void record(String event, Object... args) {
|
|
||||||
log.append(event);
|
|
||||||
for (Object arg : args) {
|
|
||||||
log.append(' ').append(arg);
|
|
||||||
}
|
|
||||||
log.append(' ').append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
String getLog() {
|
|
||||||
return log.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
public class JsonStringTest {
|
|
||||||
|
|
||||||
private StringWriter stringWriter;
|
|
||||||
|
|
||||||
private JsonWriter jsonWriter;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
stringWriter = new StringWriter();
|
|
||||||
jsonWriter = new JsonWriter(stringWriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void constructorFailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> {
|
|
||||||
new JsonString(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void write() throws IOException {
|
|
||||||
new JsonString("foo").write(jsonWriter);
|
|
||||||
assertEquals("\"foo\"", stringWriter.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void writeEscapesStrings() throws IOException {
|
|
||||||
new JsonString("foo\\bar").write(jsonWriter);
|
|
||||||
assertEquals("\"foo\\\\bar\"", stringWriter.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isString() {
|
|
||||||
assertTrue(new JsonString("foo").isString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void asString() {
|
|
||||||
assertEquals("foo", new JsonString("foo").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsTrueForSameInstance() {
|
|
||||||
JsonString string = new JsonString("foo");
|
|
||||||
assertEquals(string, string);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsTrueForEqualStrings() {
|
|
||||||
assertEquals(new JsonString("foo"), new JsonString("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsFalseForDifferentStrings() {
|
|
||||||
assertNotEquals(new JsonString(""), new JsonString("foo"));
|
|
||||||
assertNotEquals(new JsonString("foo"), new JsonString("bar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsFalseForNull() {
|
|
||||||
assertNotEquals(null, new JsonString("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsFalseForSubclass() {
|
|
||||||
assertNotEquals(new JsonString("foo"), new JsonString("foo") {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodeEqualsForEqualStrings() {
|
|
||||||
assertEquals(new JsonString("foo").hashCode(), new JsonString("foo").hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void hashCodeDiffersForDifferentStrings() {
|
|
||||||
assertNotEquals(new JsonString("").hashCode(), new JsonString("foo").hashCode());
|
|
||||||
assertNotEquals(new JsonString("foo").hashCode(), new JsonString("bar").hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,219 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
|
|
||||||
public class JsonTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void literalConstants() {
|
|
||||||
assertTrue(JsonLiteral.NULL.isNull());
|
|
||||||
assertTrue(JsonLiteral.TRUE.isTrue());
|
|
||||||
assertTrue(JsonLiteral.FALSE.isFalse());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueInt() {
|
|
||||||
assertEquals("0", Json.of(0).toString());
|
|
||||||
assertEquals("23", Json.of(23).toString());
|
|
||||||
assertEquals("-1", Json.of(-1).toString());
|
|
||||||
assertEquals("2147483647", Json.of(Integer.MAX_VALUE).toString());
|
|
||||||
assertEquals("-2147483648", Json.of(Integer.MIN_VALUE).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueLong() {
|
|
||||||
assertEquals("0", Json.of(0L).toString());
|
|
||||||
assertEquals("9223372036854775807", Json.of(Long.MAX_VALUE).toString());
|
|
||||||
assertEquals("-9223372036854775808", Json.of(Long.MIN_VALUE).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueFloat() {
|
|
||||||
assertEquals("23.5", Json.of(23.5f).toString());
|
|
||||||
assertEquals("-3.1416", Json.of(-3.1416f).toString());
|
|
||||||
assertEquals("1.23E-6", Json.of(0.00000123f).toString());
|
|
||||||
assertEquals("-1.23E7", Json.of(-12300000f).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueFloatCutsOffPointZero() {
|
|
||||||
assertEquals("0", Json.of(0f).toString());
|
|
||||||
assertEquals("-1", Json.of(-1f).toString());
|
|
||||||
assertEquals("10", Json.of(10f).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueFloatFailsWithInfinity() {
|
|
||||||
String message = "Infinite and NaN values not permitted in JSON";
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class,
|
|
||||||
() -> Json.of(Float.POSITIVE_INFINITY), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuefloatfailsWithNaN() {
|
|
||||||
String message = "Infinite and NaN values not permitted in JSON";
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class,
|
|
||||||
() -> Json.of(Float.NaN), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueDouble() {
|
|
||||||
assertEquals("23.5", Json.of(23.5d).toString());
|
|
||||||
assertEquals("3.1416", Json.of(3.1416d).toString());
|
|
||||||
assertEquals("1.23E-6", Json.of(0.00000123d).toString());
|
|
||||||
assertEquals("1.7976931348623157E308", Json.of(1.7976931348623157E308d).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueDoublecutsOffPointZero() {
|
|
||||||
assertEquals("0", Json.of(0d).toString());
|
|
||||||
assertEquals("-1", Json.of(-1d).toString());
|
|
||||||
assertEquals("10", Json.of(10d).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuedoublefailsWithInfinity() {
|
|
||||||
String message = "Infinite and NaN values not permitted in JSON";
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class,
|
|
||||||
() -> Json.of(Double.POSITIVE_INFINITY), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuedoublefailsWithNaN() {
|
|
||||||
String message = "Infinite and NaN values not permitted in JSON";
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class,
|
|
||||||
() -> Json.of(Double.NaN), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valueboolean() {
|
|
||||||
assertSame(JsonLiteral.TRUE, Json.of(true));
|
|
||||||
assertSame(JsonLiteral.FALSE, Json.of(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuestring() {
|
|
||||||
assertEquals("", Json.of("").asString());
|
|
||||||
assertEquals("Hello", Json.of("Hello").asString());
|
|
||||||
assertEquals("\"Hello\"", Json.of("\"Hello\"").asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void valuestringtoleratesNull() {
|
|
||||||
assertSame(JsonLiteral.NULL, Json.of(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void array() {
|
|
||||||
assertEquals(new JsonArray(), Json.array());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayint() {
|
|
||||||
assertEquals(new JsonArray().add(23), Json.array(23));
|
|
||||||
assertEquals(new JsonArray().add(23).add(42), Json.array(23, 42));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayintfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((int[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraylong() {
|
|
||||||
assertEquals(new JsonArray().add(23L), Json.array(23L));
|
|
||||||
assertEquals(new JsonArray().add(23L).add(42L), Json.array(23L, 42L));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraylongfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((long[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayfloat() {
|
|
||||||
assertEquals(new JsonArray().add(3.14f), Json.array(3.14f));
|
|
||||||
assertEquals(new JsonArray().add(3.14f).add(1.41f), Json.array(3.14f, 1.41f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayfloatfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((float[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraydouble() {
|
|
||||||
assertEquals(new JsonArray().add(3.14d), Json.array(3.14d));
|
|
||||||
assertEquals(new JsonArray().add(3.14d).add(1.41d), Json.array(3.14d, 1.41d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraydoublefailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((double[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arrayboolean() {
|
|
||||||
assertEquals(new JsonArray().add(true), Json.array(true));
|
|
||||||
assertEquals(new JsonArray().add(true).add(false), Json.array(true, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraybooleanfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((boolean[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraystring() {
|
|
||||||
assertEquals(new JsonArray().add("foo"), Json.array("foo"));
|
|
||||||
assertEquals(new JsonArray().add("foo").add("bar"), Json.array("foo", "bar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void arraystringfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.array((String[]) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void object() {
|
|
||||||
assertEquals(new JsonObject(), Json.object());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsestring() throws IOException {
|
|
||||||
assertEquals(Json.of(23), Json.parse("23"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parsestringfailsWithNull() {
|
|
||||||
Assertions.assertThrows(NullPointerException.class, () -> Json.parse((String) null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseReader() throws IOException {
|
|
||||||
Reader reader = new StringReader("23");
|
|
||||||
assertEquals(Json.of(23), Json.parse(reader));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.xbib.marc.StreamMatcher.assertStream;
|
import static org.xbib.marc.StreamMatcher.assertStream;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -43,8 +42,6 @@ import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
public class MarcJsonWriterTest {
|
public class MarcJsonWriterTest {
|
||||||
|
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2016-2022 Jörg Prante <joergprante@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed 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
|
|
||||||
*
|
|
||||||
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
|
|
||||||
*
|
|
||||||
* 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.xbib.marc.json;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.xbib.marc.json.JsonWriterConfig.prettyPrint;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class PrettyPrintTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_emptyArray() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonArray().write(output);
|
|
||||||
assertEquals("[\n \n]", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_emptyObject() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonObject().write(output);
|
|
||||||
assertEquals("{\n \n}", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_array() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonArray().add(23).add(42).write(output);
|
|
||||||
assertEquals("[\n 23,\n 42\n]", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_nestedArray() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonArray().add(23).add(new JsonArray().add(42)).write(output);
|
|
||||||
assertEquals("[\n 23,\n [\n 42\n ]\n]", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_object() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonObject().add("a", 23).add("b", 42).write(output);
|
|
||||||
assertEquals("{\n \"a\": 23,\n \"b\": 42\n}", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_nestedObject() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw);
|
|
||||||
new JsonObject().add("a", 23).add("b", new JsonObject().add("c", 42)).write(output);
|
|
||||||
assertEquals("{\n \"a\": 23,\n \"b\": {\n \"c\": 42\n }\n}", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_zero() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(0).createWriter(sw);
|
|
||||||
new JsonArray().add(23).add(42).write(output);
|
|
||||||
assertEquals("[\n23,\n42\n]", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_one() throws IOException {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
JsonWriter output = JsonWriterConfig.prettyPrint(1).createWriter(sw);
|
|
||||||
new JsonArray().add(23).add(42).write(output);
|
|
||||||
assertEquals("[\n 23,\n 42\n]", sw.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_failsWithNegativeValues() {
|
|
||||||
try {
|
|
||||||
prettyPrint(-1);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
assertTrue(ex.getMessage().toLowerCase(Locale.US).contains("negative"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndentWithSpaces_createsIndependentInstances() {
|
|
||||||
Writer writer = mock(Writer.class);
|
|
||||||
JsonWriterConfig config = prettyPrint(1);
|
|
||||||
Object instance1 = config.createWriter(writer);
|
|
||||||
Object instance2 = config.createWriter(writer);
|
|
||||||
assertNotSame(instance1, instance2);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue