add JSON reader and writer to allow duplicate keys, closes #8
This commit is contained in:
parent
f17e9c97b4
commit
414abce68f
35 changed files with 6823 additions and 96 deletions
|
@ -46,3 +46,12 @@ https://github.com/marc4j/marc4j/tree/master/test/resources
|
|||
|
||||
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
|
||||
|
||||
-----------------
|
||||
|
|
|
@ -24,6 +24,9 @@ dependencies {
|
|||
testImplementation("com.github.stefanbirkner:system-rules:${project.property('system-rules.version')}") {
|
||||
exclude module: 'junit'
|
||||
}
|
||||
testImplementation("org.mockito:mockito-core:${project.property('mockito.version')}") {
|
||||
exclude group: 'org.hamcrest'
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
|
|
|
@ -14,3 +14,4 @@ junit4.version = 4.12
|
|||
xalan.version = 2.7.2
|
||||
xmlunit-matchers.version = 2.6.3
|
||||
system-rules.version = 1.19.0
|
||||
mockito.version = 3.1.0
|
||||
|
|
280
src/main/java/org/xbib/marc/json/Json.java
Executable file
280
src/main/java/org/xbib/marc/json/Json.java
Executable file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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;
|
||||
}
|
||||
|
||||
}
|
398
src/main/java/org/xbib/marc/json/JsonArray.java
Executable file
398
src/main/java/org/xbib/marc/json/JsonArray.java
Executable file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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 <tt>true</tt> 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);
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/org/xbib/marc/json/JsonDefaultHandler.java
Normal file
111
src/main/java/org/xbib/marc/json/JsonDefaultHandler.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonDefaultHandler implements JsonHandler<JsonArray, JsonObject> {
|
||||
|
||||
protected JsonValue value;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
30
src/main/java/org/xbib/marc/json/JsonException.java
Executable file
30
src/main/java/org/xbib/marc/json/JsonException.java
Executable file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
public class JsonException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -3386151672072419281L;
|
||||
|
||||
JsonException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
JsonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
197
src/main/java/org/xbib/marc/json/JsonHandler.java
Executable file
197
src/main/java/org/xbib/marc/json/JsonHandler.java
Executable file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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);
|
||||
|
||||
}
|
92
src/main/java/org/xbib/marc/json/JsonLiteral.java
Executable file
92
src/main/java/org/xbib/marc/json/JsonLiteral.java
Executable file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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);
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/org/xbib/marc/json/JsonMapper.java
Normal file
111
src/main/java/org/xbib/marc/json/JsonMapper.java
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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;
|
||||
}
|
||||
}
|
115
src/main/java/org/xbib/marc/json/JsonNumber.java
Executable file
115
src/main/java/org/xbib/marc/json/JsonNumber.java
Executable file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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);
|
||||
}
|
||||
|
||||
}
|
786
src/main/java/org/xbib/marc/json/JsonObject.java
Executable file
786
src/main/java/org/xbib/marc/json/JsonObject.java
Executable file
|
@ -0,0 +1,786 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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>
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static class Member {
|
||||
|
||||
private final String name;
|
||||
private final JsonValue value;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
461
src/main/java/org/xbib/marc/json/JsonReader.java
Executable file
461
src/main/java/org/xbib/marc/json/JsonReader.java
Executable file
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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;
|
||||
}
|
||||
|
||||
}
|
60
src/main/java/org/xbib/marc/json/JsonString.java
Executable file
60
src/main/java/org/xbib/marc/json/JsonString.java
Executable file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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);
|
||||
}
|
||||
|
||||
}
|
297
src/main/java/org/xbib/marc/json/JsonValue.java
Executable file
297
src/main/java/org/xbib/marc/json/JsonValue.java
Executable file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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>
|
||||
*/
|
||||
abstract class JsonValue {
|
||||
|
||||
JsonValue() {
|
||||
// prevent subclasses outside of this package
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
141
src/main/java/org/xbib/marc/json/JsonWriter.java
Executable file
141
src/main/java/org/xbib/marc/json/JsonWriter.java
Executable file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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]};
|
||||
}
|
||||
}
|
158
src/main/java/org/xbib/marc/json/JsonWriterConfig.java
Executable file
158
src/main/java/org/xbib/marc/json/JsonWriterConfig.java
Executable file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.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
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -65,15 +66,15 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
|
||||
private final Lock lock;
|
||||
|
||||
private final StringBuilder sb;
|
||||
|
||||
private Writer writer;
|
||||
|
||||
private JsonWriter jsonWriter;
|
||||
|
||||
private Marc.Builder builder;
|
||||
|
||||
private boolean fatalErrors;
|
||||
|
||||
private Style style;
|
||||
private EnumSet<Style> style;
|
||||
|
||||
private Exception exception;
|
||||
|
||||
|
@ -96,47 +97,46 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
private boolean top;
|
||||
|
||||
public MarcJsonWriter(OutputStream out) {
|
||||
this(out, Style.ARRAY);
|
||||
this(out, EnumSet.of(Style.ARRAY));
|
||||
}
|
||||
|
||||
public MarcJsonWriter(OutputStream out, Style style) {
|
||||
public MarcJsonWriter(OutputStream out, EnumSet<Style> style) {
|
||||
this(out, DEFAULT_BUFFER_SIZE, style);
|
||||
}
|
||||
|
||||
public MarcJsonWriter(OutputStream out, int bufferSize, Style style) {
|
||||
public MarcJsonWriter(OutputStream out, int bufferSize, EnumSet<Style> style) {
|
||||
this(new OutputStreamWriter(out, StandardCharsets.UTF_8), style, bufferSize);
|
||||
}
|
||||
|
||||
public MarcJsonWriter(Writer writer) {
|
||||
this(writer, Style.ARRAY, DEFAULT_BUFFER_SIZE);
|
||||
this(writer, EnumSet.of(Style.ARRAY), DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
public MarcJsonWriter(Writer writer, Style style, int bufferSize) {
|
||||
public MarcJsonWriter(Writer writer, EnumSet<Style> style, int bufferSize) {
|
||||
this.writer = new BufferedWriter(writer, bufferSize);
|
||||
this.jsonWriter = new JsonWriter(this.writer);
|
||||
this.bufferSize = bufferSize;
|
||||
this.style = style;
|
||||
this.lock = new ReentrantLock();
|
||||
this.sb = new StringBuilder();
|
||||
this.builder = Marc.builder();
|
||||
this.top = true;
|
||||
}
|
||||
|
||||
public MarcJsonWriter(String fileNamePattern, int splitlimit) throws IOException {
|
||||
this(fileNamePattern, splitlimit, Style.LINES, DEFAULT_BUFFER_SIZE, false);
|
||||
this(fileNamePattern, splitlimit, EnumSet.of(Style.LINES), DEFAULT_BUFFER_SIZE, false);
|
||||
}
|
||||
|
||||
public MarcJsonWriter(String fileNamePattern, int splitlimit, Style style) throws IOException {
|
||||
public MarcJsonWriter(String fileNamePattern, int splitlimit, EnumSet<Style> style) throws IOException {
|
||||
this(fileNamePattern, splitlimit, style, DEFAULT_BUFFER_SIZE, false);
|
||||
}
|
||||
|
||||
public MarcJsonWriter(String fileNamePattern, int splitlimit, Style style, int bufferSize, boolean compress)
|
||||
public MarcJsonWriter(String fileNamePattern, int splitlimit, EnumSet<Style> style, int bufferSize, boolean compress)
|
||||
throws IOException {
|
||||
this.fileNameCounter = new AtomicInteger(0);
|
||||
this.fileNamePattern = fileNamePattern;
|
||||
this.splitlimit = splitlimit;
|
||||
this.bufferSize = bufferSize;
|
||||
this.lock = new ReentrantLock();
|
||||
this.sb = new StringBuilder();
|
||||
this.builder = Marc.builder();
|
||||
this.top = true;
|
||||
this.style = style;
|
||||
|
@ -226,8 +226,12 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
|
||||
@Override
|
||||
public void beginCollection() {
|
||||
if (style == Style.ARRAY) {
|
||||
sb.append("[");
|
||||
if (style.contains(Style.ARRAY)) {
|
||||
try {
|
||||
jsonWriter.writeArrayOpen();
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,9 +266,11 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
// would confuse us. Plus, we have our own locking here on record level.
|
||||
lock.lock();
|
||||
try {
|
||||
toJson(marcRecord, sb);
|
||||
writer.write(sb.toString());
|
||||
sb.setLength(0);
|
||||
if (style.contains(Style.ALLOW_DUPLICATES)) {
|
||||
writeWithDuplicateKeys(marcRecord);
|
||||
} else {
|
||||
writeUnderlyingMap(marcRecord);
|
||||
}
|
||||
recordCounter.incrementAndGet();
|
||||
afterRecord();
|
||||
} catch (Exception e) {
|
||||
|
@ -288,12 +294,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
|
||||
@Override
|
||||
public void endCollection() {
|
||||
if (style == Style.ARRAY) {
|
||||
sb.append("]");
|
||||
if (style.contains(Style.ARRAY)) {
|
||||
try {
|
||||
jsonWriter.writeArrayClose();
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
if (style == Style.ELASTICSEARCH_BULK) {
|
||||
}
|
||||
if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||
// finish with line-feed "\n", not with System.lineSeparator()
|
||||
sb.append("\n");
|
||||
try {
|
||||
writer.write("\n");
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
flush();
|
||||
|
@ -312,36 +326,105 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
}
|
||||
|
||||
/**
|
||||
* Format MARC record as key-oriented JSON.
|
||||
*
|
||||
* @param sb a string builder to append JSON to
|
||||
* Write MARC record using fields, indicators, and subfield structures,
|
||||
* therefore allowing duplicate keys in the output.
|
||||
* @param marcRecord the MARC record
|
||||
* @throws IOException if writing fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void toJson(MarcRecord marcRecord, StringBuilder sb) {
|
||||
private void writeWithDuplicateKeys(MarcRecord marcRecord) throws IOException {
|
||||
if (marcRecord.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (top) {
|
||||
top = false;
|
||||
if (style == Style.ELASTICSEARCH_BULK) {
|
||||
if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||
writeMetaDataLine(marcRecord);
|
||||
}
|
||||
} else {
|
||||
switch (style) {
|
||||
case ARRAY:
|
||||
sb.append(",");
|
||||
break;
|
||||
case LINES:
|
||||
sb.append("\n");
|
||||
break;
|
||||
case ELASTICSEARCH_BULK:
|
||||
sb.append("\n");
|
||||
if (style.contains(Style.ARRAY)) {
|
||||
jsonWriter.writeArraySeparator();
|
||||
} else if (style.contains(Style.LINES)) {
|
||||
jsonWriter.writeLiteral("\n");
|
||||
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||
jsonWriter.writeLiteral("\n");
|
||||
writeMetaDataLine(marcRecord);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
jsonWriter.writeObjectOpen();
|
||||
jsonWriter.writeMemberName(FORMAT_TAG);
|
||||
jsonWriter.writeMemberSeparator();
|
||||
jsonWriter.writeString(marcRecord.getFormat());
|
||||
jsonWriter.writeObjectSeparator();
|
||||
jsonWriter.writeMemberName(TYPE_TAG);
|
||||
jsonWriter.writeMemberSeparator();
|
||||
jsonWriter.writeString(marcRecord.getType());
|
||||
jsonWriter.writeObjectSeparator();
|
||||
jsonWriter.writeMemberName(LEADER_TAG);
|
||||
jsonWriter.writeMemberSeparator();
|
||||
jsonWriter.writeString(marcRecord.getRecordLabel().toString());
|
||||
jsonWriter.writeObjectSeparator();
|
||||
boolean fieldseparator = false;
|
||||
for (MarcField marcField : marcRecord.getFields()) {
|
||||
if (fieldseparator) {
|
||||
jsonWriter.writeObjectSeparator();
|
||||
}
|
||||
jsonWriter.writeMemberName(marcField.getTag());
|
||||
jsonWriter.writeMemberSeparator();
|
||||
if (marcField.isControl()) {
|
||||
jsonWriter.writeArrayOpen();
|
||||
jsonWriter.writeString(marcField.getValue());
|
||||
jsonWriter.writeArrayClose();
|
||||
} else {
|
||||
jsonWriter.writeObjectOpen();
|
||||
jsonWriter.writeMemberName(marcField.getIndicator().replace(' ', '_'));
|
||||
jsonWriter.writeMemberSeparator();
|
||||
jsonWriter.writeArrayOpen();
|
||||
boolean subfieldseparator = false;
|
||||
for (MarcField.Subfield subfield : marcField.getSubfields()) {
|
||||
if (subfieldseparator) {
|
||||
jsonWriter.writeObjectSeparator();
|
||||
}
|
||||
jsonWriter.writeObjectOpen();
|
||||
jsonWriter.writeMemberName(subfield.getId());
|
||||
jsonWriter.writeMemberSeparator();
|
||||
jsonWriter.writeString(subfield.getValue());
|
||||
jsonWriter.writeObjectClose();
|
||||
subfieldseparator = true;
|
||||
}
|
||||
jsonWriter.writeArrayClose();
|
||||
jsonWriter.writeObjectClose();
|
||||
}
|
||||
fieldseparator = true;
|
||||
}
|
||||
jsonWriter.writeObjectClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write MARC record from underlying map as key-oriented JSON.
|
||||
* @param marcRecord the MARC record
|
||||
* @throws IOException if writing fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void writeUnderlyingMap(MarcRecord marcRecord) throws IOException {
|
||||
if (marcRecord.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (top) {
|
||||
top = false;
|
||||
if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||
writeMetaDataLine(marcRecord);
|
||||
}
|
||||
} else {
|
||||
if (style.contains(Style.ARRAY)) {
|
||||
writer.write(",");
|
||||
} else if (style.contains(Style.LINES)) {
|
||||
writer.write("\n");
|
||||
} else if (style.contains(Style.ELASTICSEARCH_BULK)) {
|
||||
writer.write("\n");
|
||||
writeMetaDataLine(marcRecord);
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
int c0 = 0;
|
||||
for (Map.Entry<String, Object> tags : marcRecord.entrySet()) {
|
||||
|
@ -452,6 +535,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
c0++;
|
||||
}
|
||||
sb.append('}');
|
||||
writer.write(sb.toString());
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
|
@ -462,13 +546,6 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
writer.write(System.lineSeparator());
|
||||
}
|
||||
|
||||
private void handleException(IOException e) {
|
||||
exception = e;
|
||||
if (fatalErrors) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writer.close();
|
||||
|
@ -476,14 +553,6 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (sb.length() > 0) {
|
||||
try {
|
||||
writer.write(sb.toString());
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
sb.setLength(0);
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
|
@ -512,6 +581,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
writer = new OutputStreamWriter(compress ?
|
||||
new CompressedOutputStream(out, bufferSize) :
|
||||
new BufferedOutputStream(out, bufferSize), StandardCharsets.UTF_8);
|
||||
jsonWriter = new JsonWriter(writer);
|
||||
}
|
||||
|
||||
private void writeMetaDataLine(MarcRecord marcRecord) {
|
||||
|
@ -526,11 +596,22 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
}
|
||||
id = object.toString();
|
||||
if (index != null && indexType != null && id != null) {
|
||||
sb.append("{\"index\":{")
|
||||
.append("\"_index\":\"").append(index).append("\",")
|
||||
.append("\"_type\":\"").append(indexType).append("\",")
|
||||
.append("\"_id\":\"").append(id).append("\"}}")
|
||||
.append("\n");
|
||||
try {
|
||||
writer.write("{\"index\":{" +
|
||||
"\"_index\":\"" + index + "\"," +
|
||||
"\"_type\":\"" + indexType + "\"," +
|
||||
"\"_id\":\"" + id + "\"}}" +
|
||||
"\n");
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(IOException e) {
|
||||
exception = e;
|
||||
if (fatalErrors) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,7 +619,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo
|
|||
*
|
||||
*/
|
||||
public enum Style {
|
||||
ARRAY, LINES, ELASTICSEARCH_BULK
|
||||
ARRAY, LINES, ELASTICSEARCH_BULK, ALLOW_DUPLICATES
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.xbib.marc.xml.MarcXchangeWriter;
|
|||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -129,7 +130,7 @@ public class ConcurrencyTest {
|
|||
File file = File.createTempFile(s + ".", ".jsonlines");
|
||||
file.deleteOnExit();
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, MarcJsonWriter.Style.LINES)
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, EnumSet.of(MarcJsonWriter.Style.LINES))
|
||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||
) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.Normalizer;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -126,7 +127,7 @@ public class ZDBTest {
|
|||
OutputStream out = new FileOutputStream(file);
|
||||
MarcValueTransformers marcValueTransformers = new MarcValueTransformers();
|
||||
marcValueTransformers.setMarcValueTransformer(value -> Normalizer.normalize(value, Normalizer.Form.NFC));
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, MarcJsonWriter.Style.LINES)
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, EnumSet.of(MarcJsonWriter.Style.LINES))
|
||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||
.setMarcValueTransformers(marcValueTransformers)) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.io.File;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -57,7 +58,7 @@ public class MabXmlTest {
|
|||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (InputStream in = getClass().getResourceAsStream(s);
|
||||
MarcJsonWriter writer = new MarcJsonWriter(out,
|
||||
10, MarcJsonWriter.Style.ELASTICSEARCH_BULK)
|
||||
10, EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK))
|
||||
.setIndex("testindex", "testtype")) {
|
||||
MarcContentHandler contentHandler = new MabXMLContentHandler()
|
||||
.addNamespace("http://www.ddb.de/professionell/mabxml/mabxml-1.xsd")
|
||||
|
|
520
src/test/java/org/xbib/marc/json/JsonArrayTest.java
Executable file
520
src/test/java/org/xbib/marc/json/JsonArrayTest.java
Executable file
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonArrayTest extends TestUtil {
|
||||
|
||||
private JsonArray array;
|
||||
|
||||
private static JsonArray array(String... values) {
|
||||
JsonArray array = new JsonArray();
|
||||
for (String value : values) {
|
||||
array.add(value);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
array = new JsonArray();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyConstructorfailsWithNull() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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(expected = UnsupportedOperationException.class)
|
||||
public void iteratordoesNotAllowModification() {
|
||||
array.add(23);
|
||||
Iterator<JsonValue> iterator = array.iterator();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
@Test(expected = ConcurrentModificationException.class)
|
||||
public void iteratordetectsConcurrentModification() {
|
||||
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(expected = IndexOutOfBoundsException.class)
|
||||
public void getfailsWithInvalidIndex() {
|
||||
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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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);
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> array.set(0, (JsonValue) null));
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void setjsonfailsWithInvalidIndex() {
|
||||
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(expected = IndexOutOfBoundsException.class)
|
||||
public void removefailsWithInvalidIndex() {
|
||||
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());
|
||||
}
|
||||
}
|
133
src/test/java/org/xbib/marc/json/JsonLiteralTest.java
Executable file
133
src/test/java/org/xbib/marc/json/JsonLiteralTest.java
Executable file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.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.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"));
|
||||
}
|
||||
|
||||
}
|
77
src/test/java/org/xbib/marc/json/JsonMapperTest.java
Normal file
77
src/test/java/org/xbib/marc/json/JsonMapperTest.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class JsonMapperTest {
|
||||
|
||||
@Test
|
||||
public void mapperMapTest() throws IOException {
|
||||
String json = "{\"Hello\":\"World\"}";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("{Hello=World}", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperNumericMapTest() throws IOException {
|
||||
String json = "{\"Hello\":123}";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("{Hello=123}", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperArrayTest() throws IOException {
|
||||
String json = "[\"Hello\",\"World\"]";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("[Hello, World]", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperBooleanAndNullArrayTest() throws IOException {
|
||||
String json = "[true, false, null]";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("[true, false, null]", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperFloatArrayTest() throws IOException {
|
||||
String json = "[1.23, 4.56]";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("[1.23, 4.56]", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperIntArrayTest() throws IOException {
|
||||
String json = "[123, 456]";
|
||||
JsonValue jsonValue = Json.parse(json);
|
||||
Object object = JsonMapper.asObject(jsonValue);
|
||||
assertEquals("[123, 456]", Objects.requireNonNull(object).toString());
|
||||
}
|
||||
}
|
164
src/test/java/org/xbib/marc/json/JsonNumberTest.java
Executable file
164
src/test/java/org/xbib/marc/json/JsonNumberTest.java
Executable file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonNumberTest extends TestUtil {
|
||||
|
||||
private StringWriter output;
|
||||
private JsonWriter writer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
output = new StringWriter();
|
||||
writer = new JsonWriter(output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorfailsWithNull() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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(expected = NumberFormatException.class)
|
||||
public void asIntfailsWithExceedingValues() {
|
||||
new JsonNumber("10000000000").asInt();
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void asIntfailsWithExponent() {
|
||||
new JsonNumber("1e5").asInt();
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void asIntfailsWithFractional() {
|
||||
new JsonNumber("23.5").asInt();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asLong() {
|
||||
assertEquals(23L, new JsonNumber("23").asLong());
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void asLongfailsWithExceedingValues() {
|
||||
new JsonNumber("10000000000000000000").asLong();
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void asLongfailsWithExponent() {
|
||||
new JsonNumber("1e5").asLong();
|
||||
}
|
||||
|
||||
@Test(expected = NumberFormatException.class)
|
||||
public void asLongfailsWithFractional() {
|
||||
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());
|
||||
}
|
||||
}
|
952
src/test/java/org/xbib/marc/json/JsonObjectTest.java
Executable file
952
src/test/java/org/xbib/marc/json/JsonObjectTest.java
Executable file
|
@ -0,0 +1,952 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.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 extends TestUtil {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
object = new JsonObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyConstructorfailsWithNull() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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(expected = UnsupportedOperationException.class)
|
||||
public void namespreventsModification() {
|
||||
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(expected = NoSuchElementException.class)
|
||||
public void iteratornextFailsAtEnd() {
|
||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
||||
iterator.next();
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void iteratordoesNotAllowModification() {
|
||||
object.add("a", 23);
|
||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
@Test(expected = ConcurrentModificationException.class)
|
||||
public void iteratordetectsConcurrentModification() {
|
||||
Iterator<JsonObject.Member> iterator = object.iterator();
|
||||
object.add("a", 23);
|
||||
iterator.next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getfailsWithNullName() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, "name is null", (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, "value is null", (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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());
|
||||
}
|
||||
|
||||
}
|
810
src/test/java/org/xbib/marc/json/JsonReaderTest.java
Executable file
810
src/test/java/org/xbib/marc/json/JsonReaderTest.java
Executable file
|
@ -0,0 +1,810 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.hamcrest.core.StringStartsWith.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.xbib.marc.json.Json.parse;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonReaderTest extends TestUtil {
|
||||
|
||||
private static String join(String... strings) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String string : strings) {
|
||||
builder.append(string).append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void constructorRejectsNullHandler() {
|
||||
new JsonReader<>(null, null);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void parseStringrRjectsNull() throws IOException {
|
||||
JsonReader<Object, Object> reader = new JsonReader<>(new StringReader(null), new TestHandler());
|
||||
reader.parse();
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void parseReaderRejectsNull() throws IOException {
|
||||
JsonReader<Object, Object> reader = new JsonReader<>(null, new TestHandler());
|
||||
reader.parse();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void parseReaderRejectsNegativeBufferSize() throws IOException {
|
||||
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 = assertException(JsonException.class, (RunnableEx) 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);
|
||||
assertException(JsonException.class, (RunnableEx) () -> 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 = assertException(JsonException.class, (RunnableEx) 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 = assertException(JsonException.class, (RunnableEx) 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 = assertException(JsonException.class, (RunnableEx) 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 = assertException(JsonException.class, (Runnable) () -> {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
115
src/test/java/org/xbib/marc/json/JsonStringTest.java
Executable file
115
src/test/java/org/xbib/marc/json/JsonStringTest.java
Executable file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonStringTest extends TestUtil {
|
||||
|
||||
private StringWriter stringWriter;
|
||||
private JsonWriter jsonWriter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
stringWriter = new StringWriter();
|
||||
jsonWriter = new JsonWriter(stringWriter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_failsWithNull() {
|
||||
assertException(NullPointerException.class, null, new Runnable() {
|
||||
public void run() {
|
||||
new JsonString(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void write() throws IOException {
|
||||
new JsonString("foo").write(jsonWriter);
|
||||
|
||||
assertEquals("\"foo\"", stringWriter.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void write_escapesStrings() 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 equals_trueForSameInstance() {
|
||||
JsonString string = new JsonString("foo");
|
||||
|
||||
assertEquals(string, string);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_trueForEqualStrings() {
|
||||
assertEquals(new JsonString("foo"), new JsonString("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_falseForDifferentStrings() {
|
||||
assertNotEquals(new JsonString(""), new JsonString("foo"));
|
||||
assertNotEquals(new JsonString("foo"), new JsonString("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_falseForNull() {
|
||||
assertNotEquals(null, new JsonString("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_falseForSubclass() {
|
||||
assertNotEquals(new JsonString("foo"), new JsonString("foo") {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCode_equalsForEqualStrings() {
|
||||
assertEquals(new JsonString("foo").hashCode(), new JsonString("foo").hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCode_differsForDifferentStrings() {
|
||||
assertNotEquals(new JsonString("").hashCode(), new JsonString("foo").hashCode());
|
||||
assertNotEquals(new JsonString("foo").hashCode(), new JsonString("bar").hashCode());
|
||||
}
|
||||
}
|
234
src/test/java/org/xbib/marc/json/JsonTest.java
Executable file
234
src/test/java/org/xbib/marc/json/JsonTest.java
Executable file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonTest extends TestUtil {
|
||||
|
||||
@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";
|
||||
assertException(IllegalArgumentException.class, message,
|
||||
(Runnable) () -> Json.of(Float.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valuefloatfailsWithNaN() {
|
||||
String message = "Infinite and NaN values not permitted in JSON";
|
||||
assertException(IllegalArgumentException.class, message, (Runnable) () -> Json.of(Float.NaN));
|
||||
}
|
||||
|
||||
@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";
|
||||
assertException(IllegalArgumentException.class, message, (Runnable) () -> Json.of(Double.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valuedoublefailsWithNaN() {
|
||||
String message = "Infinite and NaN values not permitted in JSON";
|
||||
assertException(IllegalArgumentException.class, message, (Runnable) () -> Json.of(Double.NaN));
|
||||
}
|
||||
|
||||
@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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> 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() {
|
||||
assertException(NullPointerException.class, null, (Runnable) () -> {
|
||||
try {
|
||||
Json.parse((String) null);
|
||||
} catch (IOException e) {
|
||||
//
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsereader() throws IOException {
|
||||
Reader reader = new StringReader("23");
|
||||
|
||||
assertEquals(Json.of(23), Json.parse(reader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsereaderfailsWithNull() {
|
||||
assertException(NullPointerException.class, null, (RunnableEx) () -> Json.parse((Reader) null));
|
||||
}
|
||||
|
||||
}
|
163
src/test/java/org/xbib/marc/json/JsonWriterTest.java
Executable file
163
src/test/java/org/xbib/marc/json/JsonWriterTest.java
Executable file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JsonWriterTest {
|
||||
|
||||
private StringWriter output;
|
||||
private JsonWriter writer;
|
||||
|
||||
private static String string(char... chars) {
|
||||
return String.valueOf(chars);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
output = new StringWriter();
|
||||
writer = new JsonWriter(output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeLiteral() throws IOException {
|
||||
writer.writeLiteral("foo");
|
||||
assertEquals("foo", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeNumber() throws IOException {
|
||||
writer.writeNumber("23");
|
||||
assertEquals("23", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeStringempty() throws IOException {
|
||||
writer.writeString("");
|
||||
assertEquals("\"\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeStingescapesBackslashes() throws IOException {
|
||||
writer.writeString("foo\\bar");
|
||||
assertEquals("\"foo\\\\bar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeArrayParts() throws IOException {
|
||||
writer.writeArrayOpen();
|
||||
writer.writeArraySeparator();
|
||||
writer.writeArrayClose();
|
||||
assertEquals("[,]", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeObjectParts() throws IOException {
|
||||
writer.writeObjectOpen();
|
||||
writer.writeMemberSeparator();
|
||||
writer.writeObjectSeparator();
|
||||
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
|
||||
public void escapesQuotes() throws IOException {
|
||||
writer.writeString("a\"b");
|
||||
assertEquals("\"a\\\"b\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesEscapedQuotes() throws IOException {
|
||||
writer.writeString("foo\\\"bar");
|
||||
assertEquals("\"foo\\\\\\\"bar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesNewLine() throws IOException {
|
||||
writer.writeString("foo\nbar");
|
||||
assertEquals("\"foo\\nbar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesWindowsNewLine() throws IOException {
|
||||
writer.writeString("foo\r\nbar");
|
||||
assertEquals("\"foo\\r\\nbar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesTabs() throws IOException {
|
||||
writer.writeString("foo\tbar");
|
||||
assertEquals("\"foo\\tbar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesSpecialCharacters() throws IOException {
|
||||
writer.writeString("foo\u2028bar\u2029");
|
||||
assertEquals("\"foo\\u2028bar\\u2029\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesZeroCharacter() throws IOException {
|
||||
writer.writeString(string('f', 'o', 'o', (char) 0, 'b', 'a', 'r'));
|
||||
assertEquals("\"foo\\u0000bar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesEscapeCharacter() throws IOException {
|
||||
writer.writeString(string('f', 'o', 'o', (char) 27, 'b', 'a', 'r'));
|
||||
assertEquals("\"foo\\u001bbar\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesControlCharacters() throws IOException {
|
||||
writer.writeString(string((char) 1, (char) 8, (char) 15, (char) 16, (char) 31));
|
||||
assertEquals("\"\\u0001\\u0008\\u000f\\u0010\\u001f\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesFirstChar() throws IOException {
|
||||
writer.writeString(string('\\', 'x'));
|
||||
assertEquals("\"\\\\x\"", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesLastChar() throws IOException {
|
||||
writer.writeString(string('x', '\\'));
|
||||
assertEquals("\"x\\\\\"", output.toString());
|
||||
}
|
||||
}
|
|
@ -35,37 +35,13 @@ import java.io.FileOutputStream;
|
|||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.Normalizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MarcJsonWriterTest {
|
||||
|
||||
private static final Pattern quotePattern = Pattern.compile("\"", Pattern.LITERAL);
|
||||
|
||||
private static final String escapeQuote = "\\\"";
|
||||
|
||||
private static final Pattern backslashPattern = Pattern.compile("\\\\");
|
||||
|
||||
private static final String escapeBackslash = "\\\\";
|
||||
|
||||
private static String escape(String value) {
|
||||
String s = backslashPattern.matcher(value).replaceAll(Matcher.quoteReplacement(escapeBackslash));
|
||||
return quotePattern.matcher(s).replaceAll(Matcher.quoteReplacement(escapeQuote));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeJSON() {
|
||||
String s = "\"Hello world\"";
|
||||
String t = escape(s);
|
||||
assertEquals("\\\"Hello world\\\"", t);
|
||||
s = "\\P123";
|
||||
t = escape(s);
|
||||
assertEquals("\\\\P123", t);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code }MarcJsonWriter} can receive MARC fields.
|
||||
*
|
||||
|
@ -169,7 +145,7 @@ public class MarcJsonWriterTest {
|
|||
File file = File.createTempFile(s + ".", ".json");
|
||||
file.deleteOnExit();
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, MarcJsonWriter.Style.LINES)
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, EnumSet.of(MarcJsonWriter.Style.LINES))
|
||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||
) {
|
||||
|
@ -227,7 +203,8 @@ public class MarcJsonWriterTest {
|
|||
InputStream in = getClass().getResource("/org/xbib/marc//" + s).openStream();
|
||||
MarcValueTransformers marcValueTransformers = new MarcValueTransformers();
|
||||
marcValueTransformers.setMarcValueTransformer(value -> Normalizer.normalize(value, Normalizer.Form.NFC));
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter("build/bulk%d.jsonl", 3, MarcJsonWriter.Style.ELASTICSEARCH_BULK)
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter("build/bulk%d.jsonl",
|
||||
3, EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK))
|
||||
.setIndex("testindex", "testtype")) {
|
||||
writer.setMarcValueTransformers(marcValueTransformers);
|
||||
Marc.builder()
|
||||
|
@ -264,8 +241,8 @@ public class MarcJsonWriterTest {
|
|||
MarcValueTransformers marcValueTransformers = new MarcValueTransformers();
|
||||
marcValueTransformers.setMarcValueTransformer(value -> Normalizer.normalize(value, Normalizer.Form.NFC));
|
||||
// split at 3, Elasticsearch bulk format, buffer size 65536, compress = true
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter("build/bulk%d.jsonl.gz", 3,
|
||||
MarcJsonWriter.Style.ELASTICSEARCH_BULK, 65536, true)
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter("build/bulk%d.jsonl.gz",
|
||||
3, EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK), 65536, true)
|
||||
.setIndex("testindex", "testtype")) {
|
||||
writer.setMarcValueTransformers(marcValueTransformers);
|
||||
Marc.builder()
|
||||
|
@ -300,7 +277,7 @@ public class MarcJsonWriterTest {
|
|||
String s = "bundeskunsthalle.xml";
|
||||
InputStream in = getClass().getResource("/org/xbib/marc/xml/" + s).openStream();
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter("build/bk-bulk%d.jsonl", 1,
|
||||
MarcJsonWriter.Style.ELASTICSEARCH_BULK)
|
||||
EnumSet.of(MarcJsonWriter.Style.ELASTICSEARCH_BULK))
|
||||
.setIndex("testindex", "testtype")) {
|
||||
Marc.builder()
|
||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||
|
@ -318,7 +295,7 @@ public class MarcJsonWriterTest {
|
|||
File file = File.createTempFile("multi.", ".json");
|
||||
file.deleteOnExit();
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, MarcJsonWriter.Style.ARRAY)) {
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, EnumSet.of(MarcJsonWriter.Style.ARRAY))) {
|
||||
writer.beginCollection();
|
||||
try (InputStream inputStream = getClass().getResource("/org/xbib/marc/summerland.mrc").openStream()) {
|
||||
Marc.builder()
|
||||
|
@ -376,4 +353,35 @@ public class MarcJsonWriterTest {
|
|||
new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test JSON format that allows duplicate keys. This allows to format MARC in order,
|
||||
* as defined by cataloging rules.
|
||||
*
|
||||
* @throws Exception if test fails
|
||||
*/
|
||||
@Test
|
||||
public void testMarcRecordJsonWithDuplicateKeys() throws Exception {
|
||||
for (String s : new String[]{
|
||||
"test_ubl.mrc"
|
||||
}) {
|
||||
InputStream in = getClass().getResource("/org/xbib/marc/" + s).openStream();
|
||||
File file = File.createTempFile(s + ".", ".json");
|
||||
file.deleteOnExit();
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
try (MarcJsonWriter writer = new MarcJsonWriter(out, EnumSet.of(MarcJsonWriter.Style.ALLOW_DUPLICATES))
|
||||
) {
|
||||
Marc.builder()
|
||||
.setFormat(MarcXchangeConstants.MARCXCHANGE_FORMAT)
|
||||
.setType(MarcXchangeConstants.BIBLIOGRAPHIC_TYPE)
|
||||
.setInputStream(in)
|
||||
.setCharset(Charset.forName("ANSEL"))
|
||||
.setMarcListener(writer)
|
||||
.build()
|
||||
.writeCollection();
|
||||
}
|
||||
assertStream(s, getClass().getResource("/org/xbib/marc/json/" + s + ".dupkey.json").openStream(),
|
||||
new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
120
src/test/java/org/xbib/marc/json/PrettyPrintTest.java
Executable file
120
src/test/java/org/xbib/marc/json/PrettyPrintTest.java
Executable file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.xbib.marc.json.JsonWriterConfig.prettyPrint;
|
||||
|
||||
import org.junit.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);
|
||||
}
|
||||
}
|
95
src/test/java/org/xbib/marc/json/TestUtil.java
Normal file
95
src/test/java/org/xbib/marc/json/TestUtil.java
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2016 Jörg Prante
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package org.xbib.marc.json;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
public static <T extends Exception> T assertException(Class<T> type,
|
||||
String message,
|
||||
Runnable runnable) {
|
||||
return assertException(type, message, adapt(runnable));
|
||||
}
|
||||
|
||||
public static <T extends Exception> T assertException(Class<T> type,
|
||||
String message,
|
||||
RunnableEx runnable) {
|
||||
T exception = assertException(type, runnable);
|
||||
assertEquals("exception message", message, exception.getMessage());
|
||||
return exception;
|
||||
}
|
||||
|
||||
public static <T extends Exception> T assertException(Class<T> type, Runnable runnable) {
|
||||
return assertException(type, adapt(runnable));
|
||||
}
|
||||
|
||||
public static <T extends Exception> T assertException(Class<T> type, RunnableEx runnable) {
|
||||
T exception = catchException(runnable, type);
|
||||
assertNotNull("Expected exception: " + type.getName(), exception);
|
||||
return exception;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Exception> T catchException(RunnableEx runnable, Class<T> type) {
|
||||
try {
|
||||
runnable.run();
|
||||
return null;
|
||||
} catch (Exception exception) {
|
||||
if (type.isAssignableFrom(exception.getClass())) {
|
||||
return (T) exception;
|
||||
}
|
||||
String message = "Unexpected exception: " + exception.getMessage();
|
||||
throw new RuntimeException(message, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T serializeAndDeserialize(T instance) throws Exception {
|
||||
return (T) deserialize(serialize(instance));
|
||||
}
|
||||
|
||||
public static byte[] serialize(Object object) throws IOException {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
new ObjectOutputStream(outputStream).writeObject(object);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
return new ObjectInputStream(inputStream).readObject();
|
||||
}
|
||||
|
||||
private static RunnableEx adapt(final Runnable runnable) {
|
||||
return new RunnableEx() {
|
||||
public void run() {
|
||||
runnable.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface RunnableEx {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
1
src/test/resources/org/xbib/marc/test_ubl.mrc
Normal file
1
src/test/resources/org/xbib/marc/test_ubl.mrc
Normal file
|
@ -0,0 +1 @@
|
|||
04427cam a2201081 c45000010012000000030007000120050017000190070003000360080041000390150016000800160023000960200054001190200061001730240018002340350022002520350023002740350026002970350023003230400029003460410008003750440013003830820013003960840012004091000104004212450468005252500015009932640049010082640012010573000022010693360026010913370046011173380025011636550081011886890092012696890077013616890091014386890013015296890092015426890079016346890013017137000105017267000124018317000064019557730054020198560132020738560120022059820025023259690015023509830019023659690006023849820023023909840011024139830020024249840006024449000026024509000022024769000028024989000030025269000027025569000020025839000020026039000029026239000020026529000027026729000030026999000025027299000036027549500026027909500025028169500013028419500016028549500026028709500042028969500015029389500019029539500017029729500024029899500024030139500022030379500021030599500022030809500011031029500025031139510010031389510010031489510010031589510007031688520032031758520034032078520032032418520032032739800040033050-730849546DE-62720190704100209.0tu121203s2019 xx ||||| 00| ||ger c a12,N482dnb7 a10281626692DE-101 a3579026895cGb.ca. EUR 168.00 (DE)93-579-02689-5 a9783579026893cGb.ca. EUR 168.00 (DE)9978-3-579-02689-33 a9783579026893 a(DE-627)730849546 a(DE-576)9730849544 a(DE-599)DNB1028162669 a(DE-101)1028162669 aDE-627bgercDE-627erda ager cXA-DE-NW04a290qDNB a12ssgn1 aBuber, Martind1878-1965eVerfasserIn0(DE-588)1185164770(DE-627)1333991090(DE-576)2088770884aut10aWerkausgaben13pSchriften zur biblischen Religion / herausgegeben von Christian Wiese unter Mitarbeit von Heike Breitenbach ; eingeleitet von Michael Fishbane ; kommentiert von Christian Wiese und Heike Breitenbach unter Mitarbeit von Andreas Loschn1pTextcMartin Buber ; im Auftrag der Philosophischen Fakultät der Heinrich Heine Universität Düsseldorf und der Israel Acadademy of Sciences and Humanities herausgegeben von Paul Mendes-Flohr und Bernd Witte a1. Auflage 1aGüterslohbGütersloher Verlagshausc[2019] 4c© 2019 a726 Seitenc23 cm aTextbtxt2rdacontent aohne Hilfsmittel zu benutzenbn2rdamedia aBandbnc2rdacarrier 7aQuelle0(DE-588)4135952-50(DE-627)1056612360(DE-576)2096650842gnd-content00Du0(DE-588)4001515-40(DE-627)1046036660(DE-576)208843116aBibelpAltes Testament2gnd01Ds0(DE-588)4015950-40(DE-627)1046753140(DE-576)20891434XaExegese2gnd02Ds0(DE-588)4136677-30(DE-627)1056557400(DE-576)209671181aJüdische Philosophie2gnd0 5(DE-627)10Du0(DE-588)4001515-40(DE-627)1046036660(DE-576)208843116aBibelpAltes Testament2gnd11Ds0(DE-588)4059758-10(DE-627)1041314460(DE-576)209132159aTheologie2gnd1 5(DE-627)1 aWiese, Christiand1961-eHerausgeberIn0(DE-588)1214519170(DE-627)0813170690(DE-576)1812949584edt1 aFishbane, Michael A.d1943-eVerfasserIn einer Einleitung0(DE-588)1313998370(DE-627)50877294X0(DE-576)1787759164win1 aBuber, Martind1878-1965tSchriften zur biblischen Religion18w(DE-627)1165587912w(DE-576)095587918g13,1q13,142uhttp://deposit.d-nb.de/cgi-bin/dokserv?id=4186947&prov=M&dok_var=1&dok_ext=htmnVerlag: MVBqtext/htmlv2013-05-013Inhaltstext42uhttp://swbplus.bsz-bw.de/bsz730849546inh.htmmV:DE-576;B:DE-21qapplication/pdfv201906051551003Inhaltsverzeichnis a(DE-15)0025681716x1 bFHTheolx1 a(DE-15)3717597 cB a(DE-14)34144552x1 bFH1x1 a(DE-14)16659087 cB aPYŠBYYN, Mîkāʿēl aFishbane, Michael aFišbeyn, Mîḵāʾēl aBūber, Mordekaj Marṭin aBuber, Martin Mordehai aBuber, Marṭin aMpumper, Martin aBûber, Mordekay Martîn aBūbā, Marutin aBuber, Mordehai Martin aBuber, Mordekhai Marṭin aבובר, מרטין aבובר, מרדכי מרטין aChristliche Theologie aБогословие aJudentum aPhilosophie aÖstliche Philosophie aИудейская философия aAlter Bund aBibelauslegung aBibelexegese aBiblische Auslegung aBibelinterpretation aBiblische Exegese aSchriftauslegung aBibelwissenschaft aExeget aЭкзегетика bXA-AT bXA-DE bXB-IL bXY aDE-14z2019-05-06T09:35:35Z aDE-L325z2019-05-28T10:02:58Z aDE-16z2019-05-15T10:23:35Z aDE-15z2019-05-09T09:13:04Z a730849546b0k730849546o9730849544
|
Loading…
Reference in a new issue