/*
* Copyright (c) 2011-2017 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core.json;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.shareddata.Shareable;
import io.vertx.core.shareddata.impl.ClusterSerializable;
import java.time.Instant;
import java.util.*;
import java.util.stream.Stream;
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
A representation of a JSON array in Java.
Unlike some other languages Java does not have a native understanding of JSON. To enable JSON to be used easily
in Vert.x code we use this class to encapsulate the notion of a JSON array.
The implementation adheres to the RFC-7493 to support Temporal
data types as well as binary data.
Please see the documentation for more information.
Author: Tim Fox
/**
* A representation of a <a href="http://json.org/">JSON</a> array in Java.
* <p>
* Unlike some other languages Java does not have a native understanding of JSON. To enable JSON to be used easily
* in Vert.x code we use this class to encapsulate the notion of a JSON array.
*
* The implementation adheres to the <a href="http://rfc-editor.org/rfc/rfc7493.txt">RFC-7493</a> to support Temporal
* data types as well as binary data.
* <p>
* Please see the documentation for more information.
*
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class JsonArray implements Iterable<Object>, ClusterSerializable, Shareable {
private List<Object> list;
Create an instance from a String of JSON, this string must be a valid array otherwise an exception will be thrown.
If you are unsure of the value, you should use instead Json.decodeValue(String)
and check the result is a JSON array. Params: - json – the string of JSON
/**
* Create an instance from a String of JSON, this string must be a valid array otherwise an exception will be thrown.
* <p/>
* If you are unsure of the value, you should use instead {@link Json#decodeValue(String)} and check the result is
* a JSON array.
*
* @param json the string of JSON
*/
public JsonArray(String json) {
if (json == null) {
throw new NullPointerException();
}
fromJson(json);
if (list == null) {
throw new DecodeException("Invalid JSON array: " + json);
}
}
Create an empty instance
/**
* Create an empty instance
*/
public JsonArray() {
list = new ArrayList<>();
}
Create an instance from a List. The List is not copied.
Params: - list – the underlying backing list
/**
* Create an instance from a List. The List is not copied.
*
* @param list the underlying backing list
*/
public JsonArray(List list) {
if (list == null) {
throw new NullPointerException();
}
this.list = list;
}
Create an instance from a Buffer of JSON.
Params: - buf – the buffer of JSON.
/**
* Create an instance from a Buffer of JSON.
*
* @param buf the buffer of JSON.
*/
public JsonArray(Buffer buf) {
if (buf == null) {
throw new NullPointerException();
}
fromBuffer(buf);
if (list == null) {
throw new DecodeException("Invalid JSON array: " + buf);
}
}
Get the String at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to String
Returns: the String, or null if a null value present
/**
* Get the String at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the String, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to String
*/
public String getString(int pos) {
CharSequence cs = (CharSequence)list.get(pos);
return cs == null ? null : cs.toString();
}
Get the Integer at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to Integer
Returns: the Integer, or null if a null value present
/**
* Get the Integer at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the Integer, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to Integer
*/
public Integer getInteger(int pos) {
Number number = (Number)list.get(pos);
if (number == null) {
return null;
} else if (number instanceof Integer) {
return (Integer)number; // Avoids unnecessary unbox/box
} else {
return number.intValue();
}
}
Get the Long at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to Long
Returns: the Long, or null if a null value present
/**
* Get the Long at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the Long, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to Long
*/
public Long getLong(int pos) {
Number number = (Number)list.get(pos);
if (number == null) {
return null;
} else if (number instanceof Long) {
return (Long)number; // Avoids unnecessary unbox/box
} else {
return number.longValue();
}
}
Get the Double at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to Double
Returns: the Double, or null if a null value present
/**
* Get the Double at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the Double, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to Double
*/
public Double getDouble(int pos) {
Number number = (Number)list.get(pos);
if (number == null) {
return null;
} else if (number instanceof Double) {
return (Double)number; // Avoids unnecessary unbox/box
} else {
return number.doubleValue();
}
}
Get the Float at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to Float
Returns: the Float, or null if a null value present
/**
* Get the Float at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the Float, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to Float
*/
public Float getFloat(int pos) {
Number number = (Number)list.get(pos);
if (number == null) {
return null;
} else if (number instanceof Float) {
return (Float)number; // Avoids unnecessary unbox/box
} else {
return number.floatValue();
}
}
Get the Boolean at position pos
in the array, Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to Integer
Returns: the Boolean, or null if a null value present
/**
* Get the Boolean at position {@code pos} in the array,
*
* @param pos the position in the array
* @return the Boolean, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to Integer
*/
public Boolean getBoolean(int pos) {
return (Boolean)list.get(pos);
}
Get the JsonObject at position pos
in the array. Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to JsonObject
Returns: the JsonObject, or null if a null value present
/**
* Get the JsonObject at position {@code pos} in the array.
*
* @param pos the position in the array
* @return the JsonObject, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to JsonObject
*/
public JsonObject getJsonObject(int pos) {
Object val = list.get(pos);
if (val instanceof Map) {
val = new JsonObject((Map)val);
}
return (JsonObject)val;
}
Get the JsonArray at position pos
in the array. Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to JsonArray
Returns: the Integer, or null if a null value present
/**
* Get the JsonArray at position {@code pos} in the array.
*
* @param pos the position in the array
* @return the Integer, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to JsonArray
*/
public JsonArray getJsonArray(int pos) {
Object val = list.get(pos);
if (val instanceof List) {
val = new JsonArray((List)val);
}
return (JsonArray)val;
}
Get the byte[] at position pos
in the array.
JSON itself has no notion of a binary, so this method assumes there is a String value and
it contains a Base64 encoded binary, which it decodes if found and returns.
This method should be used in conjunction with add(byte[])
Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to String
- IllegalArgumentException – if the String value is not a legal Base64 encoded value
Returns: the byte[], or null if a null value present
/**
* Get the byte[] at position {@code pos} in the array.
* <p>
* JSON itself has no notion of a binary, so this method assumes there is a String value and
* it contains a Base64 encoded binary, which it decodes if found and returns.
* <p>
* This method should be used in conjunction with {@link #add(byte[])}
*
* @param pos the position in the array
* @return the byte[], or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to String
* @throws java.lang.IllegalArgumentException if the String value is not a legal Base64 encoded value
*/
public byte[] getBinary(int pos) {
String val = (String)list.get(pos);
if (val == null) {
return null;
} else {
return Base64.getDecoder().decode(val);
}
}
Get the Instant at position pos
in the array.
JSON itself has no notion of a temporal types, this extension complies to the RFC-7493, so this method assumes
there is a String value and it contains an ISO 8601 encoded date and time format such as "2017-04-03T10:25:41Z",
which it decodes if found and returns.
This method should be used in conjunction with add(Instant)
Params: - pos – the position in the array
Throws: - ClassCastException – if the value cannot be converted to String
- DateTimeParseException – if the String value is not a legal ISO 8601 encoded value
Returns: the Instant, or null if a null value present
/**
* Get the Instant at position {@code pos} in the array.
* <p>
* JSON itself has no notion of a temporal types, this extension complies to the RFC-7493, so this method assumes
* there is a String value and it contains an ISO 8601 encoded date and time format such as "2017-04-03T10:25:41Z",
* which it decodes if found and returns.
* <p>
* This method should be used in conjunction with {@link #add(Instant)}
*
* @param pos the position in the array
* @return the Instant, or null if a null value present
* @throws java.lang.ClassCastException if the value cannot be converted to String
* @throws java.time.format.DateTimeParseException if the String value is not a legal ISO 8601 encoded value
*/
public Instant getInstant(int pos) {
String val = (String)list.get(pos);
if (val == null) {
return null;
} else {
return Instant.from(ISO_INSTANT.parse(val));
}
}
Get the Object value at position pos
in the array. Params: - pos – the position in the array
Returns: the Integer, or null if a null value present
/**
* Get the Object value at position {@code pos} in the array.
*
* @param pos the position in the array
* @return the Integer, or null if a null value present
*/
public Object getValue(int pos) {
Object val = list.get(pos);
if (val instanceof Map) {
val = new JsonObject((Map)val);
} else if (val instanceof List) {
val = new JsonArray((List)val);
}
return val;
}
Is there a null value at position pos?
Params: - pos – the position in the array
Returns: true if null value present, false otherwise
/**
* Is there a null value at position pos?
*
* @param pos the position in the array
* @return true if null value present, false otherwise
*/
public boolean hasNull(int pos) {
return list.get(pos) == null;
}
Add an enum to the JSON array.
JSON has no concept of encoding Enums, so the Enum will be converted to a String using the Enum.name
method and the value added as a String.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add an enum to the JSON array.
* <p>
* JSON has no concept of encoding Enums, so the Enum will be converted to a String using the {@link java.lang.Enum#name}
* method and the value added as a String.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Enum value) {
list.add(value != null ? value.name() : null);
return this;
}
Add a CharSequence to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a CharSequence to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(CharSequence value) {
list.add(value != null ? value.toString() : null);
return this;
}
Add a String to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a String to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(String value) {
list.add(value);
return this;
}
Add an Integer to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add an Integer to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Integer value) {
list.add(value);
return this;
}
Add a Long to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a Long to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Long value) {
list.add(value);
return this;
}
Add a Double to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a Double to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Double value) {
list.add(value);
return this;
}
Add a Float to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a Float to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Float value) {
list.add(value);
return this;
}
Add a Boolean to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a Boolean to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Boolean value) {
list.add(value);
return this;
}
Add a null value to the JSON array.
Returns: a reference to this, so the API can be used fluently
/**
* Add a null value to the JSON array.
*
* @return a reference to this, so the API can be used fluently
*/
public JsonArray addNull() {
list.add(null);
return this;
}
Add a JSON object to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a JSON object to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(JsonObject value) {
list.add(value);
return this;
}
Add another JSON array to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add another JSON array to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(JsonArray value) {
list.add(value);
return this;
}
Add a binary value to the JSON array.
JSON has no notion of binary so the binary will be base64 encoded to a String, and the String added.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a binary value to the JSON array.
* <p>
* JSON has no notion of binary so the binary will be base64 encoded to a String, and the String added.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(byte[] value) {
list.add(value != null ? Base64.getEncoder().encodeToString(value) : null);
return this;
}
Add a Instant value to the JSON array.
JSON has no notion of Temporal data so the Instant will be ISOString encoded, and the String added.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add a Instant value to the JSON array.
* <p>
* JSON has no notion of Temporal data so the Instant will be ISOString encoded, and the String added.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Instant value) {
list.add(value != null ? ISO_INSTANT.format(value) : null);
return this;
}
Add an Object to the JSON array.
Params: - value – the value
Returns: a reference to this, so the API can be used fluently
/**
* Add an Object to the JSON array.
*
* @param value the value
* @return a reference to this, so the API can be used fluently
*/
public JsonArray add(Object value) {
value = Json.checkAndCopy(value, false);
list.add(value);
return this;
}
Appends all of the elements in the specified array to the end of this JSON array.
Params: - array – the array
Returns: a reference to this, so the API can be used fluently
/**
* Appends all of the elements in the specified array to the end of this JSON array.
*
* @param array the array
* @return a reference to this, so the API can be used fluently
*/
public JsonArray addAll(JsonArray array) {
list.addAll(array.list);
return this;
}
Does the JSON array contain the specified value? This method will scan the entire array until it finds a value
or reaches the end.
Params: - value – the value
Returns: true if it contains the value, false if not
/**
* Does the JSON array contain the specified value? This method will scan the entire array until it finds a value
* or reaches the end.
*
* @param value the value
* @return true if it contains the value, false if not
*/
public boolean contains(Object value) {
return list.contains(value);
}
Remove the specified value from the JSON array. This method will scan the entire array until it finds a value
or reaches the end.
Params: - value – the value to remove
Returns: true if it removed it, false if not found
/**
* Remove the specified value from the JSON array. This method will scan the entire array until it finds a value
* or reaches the end.
*
* @param value the value to remove
* @return true if it removed it, false if not found
*/
public boolean remove(Object value) {
return list.remove(value);
}
Remove the value at the specified position in the JSON array.
Params: - pos – the position to remove the value at
Returns: the removed value if removed, null otherwise. If the value is a Map, a JsonObject
is built from this Map and returned. It the value is a List, a JsonArray
is built form this List and returned.
/**
* Remove the value at the specified position in the JSON array.
*
* @param pos the position to remove the value at
* @return the removed value if removed, null otherwise. If the value is a Map, a {@link JsonObject} is built from
* this Map and returned. It the value is a List, a {@link JsonArray} is built form this List and returned.
*/
public Object remove(int pos) {
Object removed = list.remove(pos);
if (removed instanceof Map) {
return new JsonObject((Map) removed);
} else if (removed instanceof ArrayList) {
return new JsonArray((List) removed);
}
return removed;
}
Get the number of values in this JSON array
Returns: the number of items
/**
* Get the number of values in this JSON array
*
* @return the number of items
*/
public int size() {
return list.size();
}
Are there zero items in this JSON array?
Returns: true if zero, false otherwise
/**
* Are there zero items in this JSON array?
*
* @return true if zero, false otherwise
*/
public boolean isEmpty() {
return list.isEmpty();
}
Get the unerlying List
Returns: the underlying List
/**
* Get the unerlying List
*
* @return the underlying List
*/
public List getList() {
return list;
}
Remove all entries from the JSON array
Returns: a reference to this, so the API can be used fluently
/**
* Remove all entries from the JSON array
*
* @return a reference to this, so the API can be used fluently
*/
public JsonArray clear() {
list.clear();
return this;
}
Get an Iterator over the values in the JSON array
Returns: an iterator
/**
* Get an Iterator over the values in the JSON array
*
* @return an iterator
*/
@Override
public Iterator<Object> iterator() {
return new Iter(list.iterator());
}
Encode the JSON array to a string
Returns: the string encoding
/**
* Encode the JSON array to a string
*
* @return the string encoding
*/
public String encode() {
return Json.encode(list);
}
Encode this JSON object as buffer.
Returns: the buffer encoding.
/**
* Encode this JSON object as buffer.
*
* @return the buffer encoding.
*/
public Buffer toBuffer() {
return Json.encodeToBuffer(list);
}
Encode the JSON array prettily as a string
Returns: the string encoding
/**
* Encode the JSON array prettily as a string
*
* @return the string encoding
*/
public String encodePrettily() {
return Json.encodePrettily(list);
}
Make a copy of the JSON array
Returns: a copy
/**
* Make a copy of the JSON array
*
* @return a copy
*/
@Override
public JsonArray copy() {
List<Object> copiedList = new ArrayList<>(list.size());
for (Object val: list) {
val = Json.checkAndCopy(val, true);
copiedList.add(val);
}
return new JsonArray(copiedList);
}
Get a Stream over the entries in the JSON array
Returns: a Stream
/**
* Get a Stream over the entries in the JSON array
*
* @return a Stream
*/
public Stream<Object> stream() {
return Json.asStream(iterator());
}
@Override
public String toString() {
return encode();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
return arrayEquals(list, o);
}
static boolean arrayEquals(List<?> l1, Object o2) {
List<?> l2;
if (o2 instanceof JsonArray) {
l2 = ((JsonArray) o2).list;
} else if (o2 instanceof List<?>) {
l2 = (List<?>) o2;
} else {
return false;
}
if (l1.size() != l2.size())
return false;
Iterator<?> iter = l2.iterator();
for (Object entry : l1) {
Object other = iter.next();
if (entry == null) {
if (other != null) {
return false;
}
} else if (!JsonObject.equals(entry, other)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public void writeToBuffer(Buffer buffer) {
String encoded = encode();
byte[] bytes = encoded.getBytes();
buffer.appendInt(bytes.length);
buffer.appendBytes(bytes);
}
@Override
public int readFromBuffer(int pos, Buffer buffer) {
int length = buffer.getInt(pos);
int start = pos + 4;
String encoded = buffer.getString(start, start + length);
fromJson(encoded);
return pos + length + 4;
}
private void fromJson(String json) {
list = Json.decodeValue(json, List.class);
}
private void fromBuffer(Buffer buf) {
list = Json.decodeValue(buf, List.class);
}
private class Iter implements Iterator<Object> {
final Iterator<Object> listIter;
Iter(Iterator<Object> listIter) {
this.listIter = listIter;
}
@Override
public boolean hasNext() {
return listIter.hasNext();
}
@Override
public Object next() {
Object val = listIter.next();
if (val instanceof Map) {
val = new JsonObject((Map)val);
} else if (val instanceof List) {
val = new JsonArray((List)val);
}
return val;
}
@Override
public void remove() {
listIter.remove();
}
}
}