package org.json; /* Public Domain. */ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; /** * A JSONArray is an ordered sequence of values. Its external text form is a string wrapped in square brackets with * commas separating the values. The internal form is an object having get and opt methods for * accessing the values by index, and put methods for adding or replacing values. The values can be any of * these types: * Boolean, JSONArray, JSONObject, * Number, String, or the * JSONObject.NULL object. *

* The constructor can convert a JSON text into a Java object. The * toString method converts to JSON text. *

* A get method returns a value if one can be found, and throws an exception if one cannot be found. An * opt method returns a default value instead of throwing an exception, and so is useful for obtaining * optional values. *

* The generic get() and opt() methods return an object which you can cast or query for type. * There are also typed * get and opt methods that do type checking and type * coercion for you. *

* The texts produced by the toString methods strictly conform to JSON syntax rules. The constructors are * more forgiving in the texts they will accept: *

* * @author JSON.org * @version 2016-08/15 */ public class JSONArray implements Iterable { /** * The arrayList where the JSONArray's properties are kept. */ private final ArrayList myArrayList; /** * Construct an empty JSONArray. */ public JSONArray() { this.myArrayList = new ArrayList(); } /** * Constructs a JSONArray from a JSONTokener. *

* This constructor reads the JSONTokener to parse a JSON array. It uses the default JSONParserConfiguration. * * @param x A JSONTokener * @throws JSONException If there is a syntax error. */ public JSONArray(JSONTokener x) throws JSONException { this(x, new JSONParserConfiguration()); } /** * Constructs a JSONArray from a JSONTokener and a JSONParserConfiguration. * JSONParserConfiguration contains strictMode turned off (false) by default. * * @param x A JSONTokener instance from which the JSONArray is constructed. * @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser. * @throws JSONException If a syntax error occurs during the construction of the JSONArray. */ public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException { this(); if (x.nextClean() != '[') { throw x.syntaxError("A JSONArray text must start with '['"); } char nextChar = x.nextClean(); if (nextChar == 0) { // array is unclosed. No ']' found, instead EOF throw x.syntaxError("Expected a ',' or ']'"); } if (nextChar != ']') { x.back(); for (; ; ) { if (x.nextClean() == ',') { x.back(); this.myArrayList.add(JSONObject.NULL); } else { x.back(); if (jsonParserConfiguration.isStrictMode()) { this.myArrayList.add(x.nextValue(true)); } else { this.myArrayList.add(x.nextValue()); } } switch (x.nextClean()) { case 0: // array is unclosed. No ']' found, instead EOF throw x.syntaxError("Expected a ',' or ']'"); case ',': nextChar = x.nextClean(); if (nextChar == 0) { // array is unclosed. No ']' found, instead EOF throw x.syntaxError("Expected a ',' or ']'"); } if (nextChar == ']') { return; } x.back(); break; case ']': if (jsonParserConfiguration.isStrictMode()) { nextChar = x.nextClean(); if (nextChar != 0) { throw x.syntaxError("invalid character found after end of array: " + nextChar); } } return; default: throw x.syntaxError("Expected a ',' or ']'"); } } } } /** * Construct a JSONArray from a source JSON text. * * @param source A string that begins with [ (left bracket) and ends with * ]  (right bracket). * @throws JSONException If there is a syntax error. */ public JSONArray(String source) throws JSONException { this(new JSONTokener(source), new JSONParserConfiguration()); } public JSONArray(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException { this(new JSONTokener(source), jsonParserConfiguration); } /** * Construct a JSONArray from a Collection. * * @param collection A Collection. */ public JSONArray(Collection collection) { this(collection, 0, new JSONParserConfiguration()); } /** * Construct a JSONArray from a Collection. * * @param collection A Collection. * @param jsonParserConfiguration Configuration object for the JSON parser */ public JSONArray(Collection collection, JSONParserConfiguration jsonParserConfiguration) { this(collection, 0, jsonParserConfiguration); } /** * Construct a JSONArray from a collection with recursion depth. * * @param collection A Collection. * @param recursionDepth Variable for tracking the count of nested object creations. * @param jsonParserConfiguration Configuration object for the JSON parser */ JSONArray(Collection collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) { throw new JSONException( "JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); } if (collection == null) { this.myArrayList = new ArrayList(); } else { this.myArrayList = new ArrayList(collection.size()); this.addAll(collection, true, recursionDepth, jsonParserConfiguration); } } /** * Construct a JSONArray from an Iterable. This is a shallow copy. * * @param iter A Iterable collection. */ public JSONArray(Iterable iter) { this(); if (iter == null) { return; } this.addAll(iter, true); } /** * Construct a JSONArray from another JSONArray. This is a shallow copy. * * @param array A array. */ public JSONArray(JSONArray array) { if (array == null) { this.myArrayList = new ArrayList(); } else { // shallow copy directly the internal array lists as any wrapping // should have been done already in the original JSONArray this.myArrayList = new ArrayList(array.myArrayList); } } /** * Construct a JSONArray from an array. * * @param array Array. If the parameter passed is null, or not an array, an exception will be thrown. * @throws JSONException If not an array or if an array value is non-finite number. * @throws NullPointerException Thrown if the array parameter is null. */ public JSONArray(Object array) throws JSONException { this(); if (!array.getClass().isArray()) { throw new JSONException( "JSONArray initial value should be a string or collection or array."); } this.addAll(array, true, 0); } /** * Construct a JSONArray with the specified initial capacity. * * @param initialCapacity the initial capacity of the JSONArray. * @throws JSONException If the initial capacity is negative. */ public JSONArray(int initialCapacity) throws JSONException { if (initialCapacity < 0) { throw new JSONException( "JSONArray initial capacity cannot be negative."); } this.myArrayList = new ArrayList(initialCapacity); } @Override public Iterator iterator() { return this.myArrayList.iterator(); } /** * Get the object value associated with an index. * * @param index The index must be between 0 and length() - 1. * @return An object value. * @throws JSONException If there is no value for the index. */ public Object get(int index) throws JSONException { Object object = this.opt(index); if (object == null) { throw new JSONException("JSONArray[" + index + "] not found."); } return object; } /** * Get the boolean value associated with an index. The string values "true" and "false" are converted to boolean. * * @param index The index must be between 0 and length() - 1. * @return The truth. * @throws JSONException If there is no value for the index or if the value is not convertible to boolean. */ public boolean getBoolean(int index) throws JSONException { Object object = this.get(index); if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object) .equalsIgnoreCase("false"))) { return false; } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object) .equalsIgnoreCase("true"))) { return true; } throw wrongValueFormatException(index, "boolean", object, null); } /** * Get the double value associated with an index. * * @param index The index must be between 0 and length() - 1. * @return The value. * @throws JSONException If the key is not found or if the value cannot be converted to a number. */ public double getDouble(int index) throws JSONException { final Object object = this.get(index); if (object instanceof Number) { return ((Number) object).doubleValue(); } try { return Double.parseDouble(object.toString()); } catch (Exception e) { throw wrongValueFormatException(index, "double", object, e); } } /** * Get the float value associated with a key. * * @param index The index must be between 0 and length() - 1. * @return The numeric value. * @throws JSONException if the key is not found or if the value is not a Number object and cannot be converted to a * number. */ public float getFloat(int index) throws JSONException { final Object object = this.get(index); if (object instanceof Number) { return ((Number) object).floatValue(); } try { return Float.parseFloat(object.toString()); } catch (Exception e) { throw wrongValueFormatException(index, "float", object, e); } } /** * Get the Number value associated with a key. * * @param index The index must be between 0 and length() - 1. * @return The numeric value. * @throws JSONException if the key is not found or if the value is not a Number object and cannot be converted to a * number. */ public Number getNumber(int index) throws JSONException { Object object = this.get(index); try { if (object instanceof Number) { return (Number) object; } return JSONObject.stringToNumber(object.toString()); } catch (Exception e) { throw wrongValueFormatException(index, "number", object, e); } } /** * Get the enum value associated with an index. * * @param Enum Type * @param clazz The type of enum to retrieve. * @param index The index must be between 0 and length() - 1. * @return The enum value at the index location * @throws JSONException if the key is not found or if the value cannot be converted to an enum. */ public > E getEnum(Class clazz, int index) throws JSONException { E val = optEnum(clazz, index); if (val == null) { // JSONException should really take a throwable argument. // If it did, I would re-implement this with the Enum.valueOf // method and place any thrown exception in the JSONException throw wrongValueFormatException(index, "enum of type " + JSONObject.quote(clazz.getSimpleName()), opt(index), null); } return val; } /** * Get the BigDecimal value associated with an index. If the value is float or double, the * {@link BigDecimal#BigDecimal(double)} constructor will be used. See notes on the constructor for conversion * issues that may arise. * * @param index The index must be between 0 and length() - 1. * @return The value. * @throws JSONException If the key is not found or if the value cannot be converted to a BigDecimal. */ public BigDecimal getBigDecimal(int index) throws JSONException { Object object = this.get(index); BigDecimal val = JSONObject.objectToBigDecimal(object, null); if (val == null) { throw wrongValueFormatException(index, "BigDecimal", object, null); } return val; } /** * Get the BigInteger value associated with an index. * * @param index The index must be between 0 and length() - 1. * @return The value. * @throws JSONException If the key is not found or if the value cannot be converted to a BigInteger. */ public BigInteger getBigInteger(int index) throws JSONException { Object object = this.get(index); BigInteger val = JSONObject.objectToBigInteger(object, null); if (val == null) { throw wrongValueFormatException(index, "BigInteger", object, null); } return val; } /** * Get the int value associated with an index. * * @param index The index must be between 0 and length() - 1. * @return The value. * @throws JSONException If the key is not found or if the value is not a number. */ public int getInt(int index) throws JSONException { final Object object = this.get(index); if (object instanceof Number) { return ((Number) object).intValue(); } try { return Integer.parseInt(object.toString()); } catch (Exception e) { throw wrongValueFormatException(index, "int", object, e); } } /** * Get the JSONArray associated with an index. * * @param index The index must be between 0 and length() - 1. * @return A JSONArray value. * @throws JSONException If there is no value for the index. or if the value is not a JSONArray */ public JSONArray getJSONArray(int index) throws JSONException { Object object = this.get(index); if (object instanceof JSONArray) { return (JSONArray) object; } throw wrongValueFormatException(index, "JSONArray", object, null); } /** * Get the JSONObject associated with an index. * * @param index subscript * @return A JSONObject value. * @throws JSONException If there is no value for the index or if the value is not a JSONObject */ public JSONObject getJSONObject(int index) throws JSONException { Object object = this.get(index); if (object instanceof JSONObject) { return (JSONObject) object; } throw wrongValueFormatException(index, "JSONObject", object, null); } /** * Get the long value associated with an index. * * @param index The index must be between 0 and length() - 1. * @return The value. * @throws JSONException If the key is not found or if the value cannot be converted to a number. */ public long getLong(int index) throws JSONException { final Object object = this.get(index); if (object instanceof Number) { return ((Number) object).longValue(); } try { return Long.parseLong(object.toString()); } catch (Exception e) { throw wrongValueFormatException(index, "long", object, e); } } /** * Get the string associated with an index. * * @param index The index must be between 0 and length() - 1. * @return A string value. * @throws JSONException If there is no string value for the index. */ public String getString(int index) throws JSONException { Object object = this.get(index); if (object instanceof String) { return (String) object; } throw wrongValueFormatException(index, "String", object, null); } /** * Determine if the value is null. * * @param index The index must be between 0 and length() - 1. * @return true if the value at the index is null, or if there is no value. */ public boolean isNull(int index) { return JSONObject.NULL.equals(this.opt(index)); } /** * Make a string from the contents of this JSONArray. The * separator string is inserted between each element. Warning: * This method assumes that the data structure is acyclical. * * @param separator A string that will be inserted between the elements. * @return a string. * @throws JSONException If the array contains an invalid number. */ public String join(String separator) throws JSONException { int len = this.length(); if (len == 0) { return ""; } StringBuilder sb = new StringBuilder( JSONObject.valueToString(this.myArrayList.get(0))); for (int i = 1; i < len; i++) { sb.append(separator) .append(JSONObject.valueToString(this.myArrayList.get(i))); } return sb.toString(); } /** * Get the number of elements in the JSONArray, included nulls. * * @return The length (or size). */ public int length() { return this.myArrayList.size(); } /** * Removes all of the elements from this JSONArray. The JSONArray will be empty after this call returns. */ public void clear() { this.myArrayList.clear(); } /** * Get the optional object value associated with an index. * * @param index The index must be between 0 and length() - 1. If not, null is returned. * @return An object value, or null if there is no object at that index. */ public Object opt(int index) { return (index < 0 || index >= this.length()) ? null : this.myArrayList .get(index); } /** * Get the optional boolean value associated with an index. It returns false if there is no value at that index, or * if the value is not Boolean.TRUE or the String "true". * * @param index The index must be between 0 and length() - 1. * @return The truth. */ public boolean optBoolean(int index) { return this.optBoolean(index, false); } /** * Get the optional boolean value associated with an index. It returns the defaultValue if there is no value at that * index or if it is not a Boolean or the String "true" or "false" (case insensitive). * * @param index The index must be between 0 and length() - 1. * @param defaultValue A boolean default. * @return The truth. */ public boolean optBoolean(int index, boolean defaultValue) { try { return this.getBoolean(index); } catch (Exception e) { return defaultValue; } } /** * Get the optional Boolean object associated with an index. It returns false if there is no value at that index, or * if the value is not Boolean.TRUE or the String "true". * * @param index The index must be between 0 and length() - 1. * @return The truth. */ public Boolean optBooleanObject(int index) { return this.optBooleanObject(index, false); } /** * Get the optional Boolean object associated with an index. It returns the defaultValue if there is no value at * that index or if it is not a Boolean or the String "true" or "false" (case insensitive). * * @param index The index must be between 0 and length() - 1. * @param defaultValue A boolean default. * @return The truth. */ public Boolean optBooleanObject(int index, Boolean defaultValue) { try { return this.getBoolean(index); } catch (Exception e) { return defaultValue; } } /** * Get the optional double value associated with an index. NaN is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The value. */ public double optDouble(int index) { return this.optDouble(index, Double.NaN); } /** * Get the optional double value associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index subscript * @param defaultValue The default value. * @return The value. */ public double optDouble(int index, double defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } final double doubleValue = val.doubleValue(); // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { // return defaultValue; // } return doubleValue; } /** * Get the optional Double object associated with an index. NaN is returned if there is no value for the index, or * if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The object. */ public Double optDoubleObject(int index) { return this.optDoubleObject(index, Double.NaN); } /** * Get the optional double value associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index subscript * @param defaultValue The default object. * @return The object. */ public Double optDoubleObject(int index, Double defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } final Double doubleValue = val.doubleValue(); // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { // return defaultValue; // } return doubleValue; } /** * Get the optional float value associated with an index. NaN is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The value. */ public float optFloat(int index) { return this.optFloat(index, Float.NaN); } /** * Get the optional float value associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index subscript * @param defaultValue The default value. * @return The value. */ public float optFloat(int index, float defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } final float floatValue = val.floatValue(); // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { // return floatValue; // } return floatValue; } /** * Get the optional Float object associated with an index. NaN is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The object. */ public Float optFloatObject(int index) { return this.optFloatObject(index, Float.NaN); } /** * Get the optional Float object associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index subscript * @param defaultValue The default object. * @return The object. */ public Float optFloatObject(int index, Float defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } final Float floatValue = val.floatValue(); // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { // return floatValue; // } return floatValue; } /** * Get the optional int value associated with an index. Zero is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The value. */ public int optInt(int index) { return this.optInt(index, 0); } /** * Get the optional int value associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default value. * @return The value. */ public int optInt(int index, int defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } return val.intValue(); } /** * Get the optional Integer object associated with an index. Zero is returned if there is no value for the index, or * if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The object. */ public Integer optIntegerObject(int index) { return this.optIntegerObject(index, 0); } /** * Get the optional Integer object associated with an index. The defaultValue is returned if there is no value for * the index, or if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default object. * @return The object. */ public Integer optIntegerObject(int index, Integer defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } return val.intValue(); } /** * Get the enum value associated with a key. * * @param Enum Type * @param clazz The type of enum to retrieve. * @param index The index must be between 0 and length() - 1. * @return The enum value at the index location or null if not found */ public > E optEnum(Class clazz, int index) { return this.optEnum(clazz, index, null); } /** * Get the enum value associated with a key. * * @param Enum Type * @param clazz The type of enum to retrieve. * @param index The index must be between 0 and length() - 1. * @param defaultValue The default in case the value is not found * @return The enum value at the index location or defaultValue if the value is not found or cannot be assigned to * clazz */ public > E optEnum(Class clazz, int index, E defaultValue) { try { Object val = this.opt(index); if (JSONObject.NULL.equals(val)) { return defaultValue; } if (clazz.isAssignableFrom(val.getClass())) { // we just checked it! @SuppressWarnings("unchecked") E myE = (E) val; return myE; } return Enum.valueOf(clazz, val.toString()); } catch (IllegalArgumentException e) { return defaultValue; } catch (NullPointerException e) { return defaultValue; } } /** * Get the optional BigInteger value associated with an index. The defaultValue is returned if there is no value for * the index, or if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default value. * @return The value. */ public BigInteger optBigInteger(int index, BigInteger defaultValue) { Object val = this.opt(index); return JSONObject.objectToBigInteger(val, defaultValue); } /** * Get the optional BigDecimal value associated with an index. The defaultValue is returned if there is no value for * the index, or if the value is not a number and cannot be converted to a number. If the value is float or double, * the {@link BigDecimal#BigDecimal(double)} constructor will be used. See notes on the constructor for conversion * issues that may arise. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default value. * @return The value. */ public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { Object val = this.opt(index); return JSONObject.objectToBigDecimal(val, defaultValue); } /** * Get the optional JSONArray associated with an index. Null is returned if there is no value at that index or if * the value is not a JSONArray. * * @param index The index must be between 0 and length() - 1. * @return A JSONArray value. */ public JSONArray optJSONArray(int index) { return this.optJSONArray(index, null); } /** * Get the optional JSONArray associated with an index. The defaultValue is returned if there is no value at that * index or if the value is not a JSONArray. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default. * @return A JSONArray value. */ public JSONArray optJSONArray(int index, JSONArray defaultValue) { Object object = this.opt(index); return object instanceof JSONArray ? (JSONArray) object : defaultValue; } /** * Get the optional JSONObject associated with an index. Null is returned if there is no value at that index or if * the value is not a JSONObject. * * @param index The index must be between 0 and length() - 1. * @return A JSONObject value. */ public JSONObject optJSONObject(int index) { return this.optJSONObject(index, null); } /** * Get the optional JSONObject associated with an index. The defaultValue is returned if there is no value at that * index or if the value is not a JSONObject. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default. * @return A JSONObject value. */ public JSONObject optJSONObject(int index, JSONObject defaultValue) { Object object = this.opt(index); return object instanceof JSONObject ? (JSONObject) object : defaultValue; } /** * Get the optional long value associated with an index. Zero is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The value. */ public long optLong(int index) { return this.optLong(index, 0); } /** * Get the optional long value associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default value. * @return The value. */ public long optLong(int index, long defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } return val.longValue(); } /** * Get the optional Long object associated with an index. Zero is returned if there is no value for the index, or if * the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @return The object. */ public Long optLongObject(int index) { return this.optLongObject(index, 0L); } /** * Get the optional Long object associated with an index. The defaultValue is returned if there is no value for the * index, or if the value is not a number and cannot be converted to a number. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default object. * @return The object. */ public Long optLongObject(int index, Long defaultValue) { final Number val = this.optNumber(index, null); if (val == null) { return defaultValue; } return val.longValue(); } /** * Get an optional {@link Number} value associated with a key, or null if there is no such key or if * the value is not a number. If the value is a string, an attempt will be made to evaluate it as a number * ({@link BigDecimal}). This method would be used in cases where type coercion of the number value is unwanted. * * @param index The index must be between 0 and length() - 1. * @return An object which is the value. */ public Number optNumber(int index) { return this.optNumber(index, null); } /** * Get an optional {@link Number} value associated with a key, or the default if there is no such key or if the * value is not a number. If the value is a string, an attempt will be made to evaluate it as a number * ({@link BigDecimal}). This method would be used in cases where type coercion of the number value is unwanted. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default. * @return An object which is the value. */ public Number optNumber(int index, Number defaultValue) { Object val = this.opt(index); if (JSONObject.NULL.equals(val)) { return defaultValue; } if (val instanceof Number) { return (Number) val; } if (val instanceof String) { try { return JSONObject.stringToNumber((String) val); } catch (Exception e) { return defaultValue; } } return defaultValue; } /** * Get the optional string value associated with an index. It returns an empty string if there is no value at that * index. If the value is not a string and is not null, then it is converted to a string. * * @param index The index must be between 0 and length() - 1. * @return A String value. */ public String optString(int index) { return this.optString(index, ""); } /** * Get the optional string associated with an index. The defaultValue is returned if the key is not found. * * @param index The index must be between 0 and length() - 1. * @param defaultValue The default value. * @return A String value. */ public String optString(int index, String defaultValue) { Object object = this.opt(index); return JSONObject.NULL.equals(object) ? defaultValue : object .toString(); } /** * Append a boolean value. This increases the array's length by one. * * @param value A boolean value. * @return this. */ public JSONArray put(boolean value) { return this.put(value ? Boolean.TRUE : Boolean.FALSE); } /** * Put a value in the JSONArray, where the value will be a JSONArray which is produced from a Collection. * * @param value A Collection value. * @return this. * @throws JSONException If the value is non-finite number. */ public JSONArray put(Collection value) { return this.put(new JSONArray(value)); } /** * Append a double value. This increases the array's length by one. * * @param value A double value. * @return this. * @throws JSONException if the value is not finite. */ public JSONArray put(double value) throws JSONException { return this.put(Double.valueOf(value)); } /** * Append a float value. This increases the array's length by one. * * @param value A float value. * @return this. * @throws JSONException if the value is not finite. */ public JSONArray put(float value) throws JSONException { return this.put(Float.valueOf(value)); } /** * Append an int value. This increases the array's length by one. * * @param value An int value. * @return this. */ public JSONArray put(int value) { return this.put(Integer.valueOf(value)); } /** * Append an long value. This increases the array's length by one. * * @param value A long value. * @return this. */ public JSONArray put(long value) { return this.put(Long.valueOf(value)); } /** * Put a value in the JSONArray, where the value will be a JSONObject which is produced from a Map. * * @param value A Map value. * @return this. * @throws JSONException If a value in the map is non-finite number. * @throws NullPointerException If a key in the map is null */ public JSONArray put(Map value) { return this.put(new JSONObject(value)); } /** * Append an object value. This increases the array's length by one. * * @param value An object value. The value should be a Boolean, Double, Integer, JSONArray, JSONObject, Long, or * String, or the JSONObject.NULL object. * @return this. * @throws JSONException If the value is non-finite number. */ public JSONArray put(Object value) { JSONObject.testValidity(value); this.myArrayList.add(value); return this; } /** * Put or replace a boolean value in the JSONArray. If the index is greater than the length of the JSONArray, then * null elements will be added as necessary to pad it out. * * @param index The subscript. * @param value A boolean value. * @return this. * @throws JSONException If the index is negative. */ public JSONArray put(int index, boolean value) throws JSONException { return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); } /** * Put a value in the JSONArray, where the value will be a JSONArray which is produced from a Collection. * * @param index The subscript. * @param value A Collection value. * @return this. * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, Collection value) throws JSONException { return this.put(index, new JSONArray(value)); } /** * Put or replace a double value. If the index is greater than the length of the JSONArray, then null elements will * be added as necessary to pad it out. * * @param index The subscript. * @param value A double value. * @return this. * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, double value) throws JSONException { return this.put(index, Double.valueOf(value)); } /** * Put or replace a float value. If the index is greater than the length of the JSONArray, then null elements will * be added as necessary to pad it out. * * @param index The subscript. * @param value A float value. * @return this. * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, float value) throws JSONException { return this.put(index, Float.valueOf(value)); } /** * Put or replace an int value. If the index is greater than the length of the JSONArray, then null elements will be * added as necessary to pad it out. * * @param index The subscript. * @param value An int value. * @return this. * @throws JSONException If the index is negative. */ public JSONArray put(int index, int value) throws JSONException { return this.put(index, Integer.valueOf(value)); } /** * Put or replace a long value. If the index is greater than the length of the JSONArray, then null elements will be * added as necessary to pad it out. * * @param index The subscript. * @param value A long value. * @return this. * @throws JSONException If the index is negative. */ public JSONArray put(int index, long value) throws JSONException { return this.put(index, Long.valueOf(value)); } /** * Put a value in the JSONArray, where the value will be a JSONObject that is produced from a Map. * * @param index The subscript. * @param value The Map value. * @return reference to self * @throws JSONException If the index is negative or if the value is an invalid number. * @throws NullPointerException If a key in the map is null */ public JSONArray put(int index, Map value) throws JSONException { this.put(index, new JSONObject(value, new JSONParserConfiguration())); return this; } /** * Put a value in the JSONArray, where the value will be a JSONObject that is produced from a Map. * * @param index The subscript * @param value The Map value. * @param jsonParserConfiguration Configuration object for the JSON parser * @return reference to self * @throws JSONException If the index is negative or if the value is an invalid number. */ public JSONArray put(int index, Map value, JSONParserConfiguration jsonParserConfiguration) throws JSONException { this.put(index, new JSONObject(value, jsonParserConfiguration)); return this; } /** * Put or replace an object value in the JSONArray. If the index is greater than the length of the JSONArray, then * null elements will be added as necessary to pad it out. * * @param index The subscript. * @param value The value to put into the array. The value should be a Boolean, Double, Integer, JSONArray, * JSONObject, Long, or String, or the JSONObject.NULL object. * @return this. * @throws JSONException If the index is negative or if the value is an invalid number. */ public JSONArray put(int index, Object value) throws JSONException { if (index < 0) { throw new JSONException("JSONArray[" + index + "] not found."); } if (index < this.length()) { JSONObject.testValidity(value); this.myArrayList.set(index, value); return this; } if (index == this.length()) { // simple append return this.put(value); } // if we are inserting past the length, we want to grow the array all at once // instead of incrementally. this.myArrayList.ensureCapacity(index + 1); while (index != this.length()) { // we don't need to test validity of NULL objects this.myArrayList.add(JSONObject.NULL); } return this.put(value); } /** * Put a collection's elements in to the JSONArray. * * @param collection A Collection. * @return this. */ public JSONArray putAll(Collection collection) { this.addAll(collection, false); return this; } /** * Put an Iterable's elements in to the JSONArray. * * @param iter An Iterable. * @return this. */ public JSONArray putAll(Iterable iter) { this.addAll(iter, false); return this; } /** * Put a JSONArray's elements in to the JSONArray. * * @param array A JSONArray. * @return this. */ public JSONArray putAll(JSONArray array) { // directly copy the elements from the source array to this one // as all wrapping should have been done already in the source. this.myArrayList.addAll(array.myArrayList); return this; } /** * Put an array's elements in to the JSONArray. * * @param array Array. If the parameter passed is null, or not an array or Iterable, an exception will be thrown. * @return this. * @throws JSONException If not an array, JSONArray, Iterable or if an value is non-finite number. * @throws NullPointerException Thrown if the array parameter is null. */ public JSONArray putAll(Object array) throws JSONException { this.addAll(array, false); return this; } /** * Creates a JSONPointer using an initialization string and tries to match it to an item within this JSONArray. For * example, given a JSONArray initialized with this document: *
     * [
     *     {"b":"c"}
     * ]
     * 
* and this JSONPointer string: *
     * "/0/b"
     * 
* Then this method will return the String "c" A JSONPointerException may be thrown from code called by this * method. * * @param jsonPointer string that can be used to create a JSONPointer * @return the item matched by the JSONPointer, otherwise null */ public Object query(String jsonPointer) { return query(new JSONPointer(jsonPointer)); } /** * Uses a user initialized JSONPointer and tries to match it to an item within this JSONArray. For example, given a * JSONArray initialized with this document: *
     * [
     *     {"b":"c"}
     * ]
     * 
* and this JSONPointer: *
     * "/0/b"
     * 
* Then this method will return the String "c" A JSONPointerException may be thrown from code called by this * method. * * @param jsonPointer string that can be used to create a JSONPointer * @return the item matched by the JSONPointer, otherwise null */ public Object query(JSONPointer jsonPointer) { return jsonPointer.queryFrom(this); } /** * Queries and returns a value from this object using {@code jsonPointer}, or returns null if the query fails due to * a missing key. * * @param jsonPointer the string representation of the JSON pointer * @return the queried value or {@code null} * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax */ public Object optQuery(String jsonPointer) { return optQuery(new JSONPointer(jsonPointer)); } /** * Queries and returns a value from this object using {@code jsonPointer}, or returns null if the query fails due to * a missing key. * * @param jsonPointer The JSON pointer * @return the queried value or {@code null} * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax */ public Object optQuery(JSONPointer jsonPointer) { try { return jsonPointer.queryFrom(this); } catch (JSONPointerException e) { return null; } } /** * Remove an index and close the hole. * * @param index The index of the element to be removed. * @return The value that was associated with the index, or null if there was no value. */ public Object remove(int index) { return index >= 0 && index < this.length() ? this.myArrayList.remove(index) : null; } /** * Determine if two JSONArrays are similar. They must contain similar sequences. * * @param other The other JSONArray * @return true if they are equal */ public boolean similar(Object other) { if (!(other instanceof JSONArray)) { return false; } int len = this.length(); if (len != ((JSONArray) other).length()) { return false; } for (int i = 0; i < len; i += 1) { Object valueThis = this.myArrayList.get(i); Object valueOther = ((JSONArray) other).myArrayList.get(i); if (valueThis == valueOther) { continue; } if (valueThis == null) { return false; } if (valueThis instanceof JSONObject) { if (!((JSONObject) valueThis).similar(valueOther)) { return false; } } else if (valueThis instanceof JSONArray) { if (!((JSONArray) valueThis).similar(valueOther)) { return false; } } else if (valueThis instanceof Number && valueOther instanceof Number) { if (!JSONObject.isNumberSimilar((Number) valueThis, (Number) valueOther)) { return false; } } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { return false; } } else if (!valueThis.equals(valueOther)) { return false; } } return true; } /** * Produce a JSONObject by combining a JSONArray of names with the values of this JSONArray. * * @param names A JSONArray containing a list of key strings. These will be paired with the values. * @return A JSONObject, or null if there are no names or if this JSONArray has no values. * @throws JSONException If any of the names are null. */ public JSONObject toJSONObject(JSONArray names) throws JSONException { if (names == null || names.isEmpty() || this.isEmpty()) { return null; } JSONObject jo = new JSONObject(names.length()); for (int i = 0; i < names.length(); i += 1) { jo.put(names.getString(i), this.opt(i)); } return jo; } /** * Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it is not possible to * produce a syntactically correct JSON text then null will be returned instead. This could occur if the array * contains an invalid number. *

* Warning: This method assumes that the data structure is acyclical. * * * @return a printable, displayable, transmittable representation of the array. */ @Override public String toString() { try { return this.toString(0); } catch (Exception e) { return null; } } /** * Make a pretty-printed JSON text of this JSONArray. * *

If

 {@code indentFactor > 0}
and the {@link JSONArray} has only * one element, then the array will be output on a single line: *
{@code [1]}
* *

If an array has 2 or more elements, then it will be output across * multiple lines:

{@code
     * [
     * 1,
     * "value 2",
     * 3
     * ]
     * }
*

* Warning: This method assumes that the data structure is acyclical. * * * @param indentFactor The number of spaces to add to each level of indentation. * @return a printable, displayable, transmittable representation of the object, beginning with * [ (left bracket) and ending with ]  (right * bracket). * @throws JSONException if a called function fails */ @SuppressWarnings("resource") public String toString(int indentFactor) throws JSONException { StringWriter sw = new StringWriter(); return this.write(sw, indentFactor, 0).toString(); } /** * Write the contents of the JSONArray as JSON text to a writer. For compactness, no whitespace is added. *

* Warning: This method assumes that the data structure is acyclical. * * * @param writer the writer object * @return The writer. * @throws JSONException if a called function fails */ public Writer write(Writer writer) throws JSONException { return this.write(writer, 0, 0); } /** * Write the contents of the JSONArray as JSON text to a writer. * *

If

{@code indentFactor > 0}
and the {@link JSONArray} has only * one element, then the array will be output on a single line: *
{@code [1]}
* *

If an array has 2 or more elements, then it will be output across * multiple lines:

{@code
     * [
     * 1,
     * "value 2",
     * 3
     * ]
     * }
*

* Warning: This method assumes that the data structure is acyclical. * * * @param writer Writes the serialized JSON * @param indentFactor The number of spaces to add to each level of indentation. * @param indent The indentation of the top level. * @return The writer. * @throws JSONException if a called function fails or unable to write */ @SuppressWarnings("resource") public Writer write(Writer writer, int indentFactor, int indent) throws JSONException { try { boolean needsComma = false; int length = this.length(); writer.write('['); if (length == 1) { try { JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent); } catch (Exception e) { throw new JSONException("Unable to write JSONArray value at index: 0", e); } } else if (length != 0) { final int newIndent = indent + indentFactor; for (int i = 0; i < length; i += 1) { if (needsComma) { writer.write(','); } if (indentFactor > 0) { writer.write('\n'); } JSONObject.indent(writer, newIndent); try { JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newIndent); } catch (Exception e) { throw new JSONException("Unable to write JSONArray value at index: " + i, e); } needsComma = true; } if (indentFactor > 0) { writer.write('\n'); } JSONObject.indent(writer, indent); } writer.write(']'); return writer; } catch (IOException e) { throw new JSONException(e); } } /** * Returns a java.util.List containing all of the elements in this array. If an element in the array is a JSONArray * or JSONObject it will also be converted to a List and a Map respectively. *

* Warning: This method assumes that the data structure is acyclical. * * @return a java.util.List containing the elements of this array */ public List toList() { List results = new ArrayList(this.myArrayList.size()); for (Object element : this.myArrayList) { if (element == null || JSONObject.NULL.equals(element)) { results.add(null); } else if (element instanceof JSONArray) { results.add(((JSONArray) element).toList()); } else if (element instanceof JSONObject) { results.add(((JSONObject) element).toMap()); } else { results.add(element); } } return results; } /** * Check if JSONArray is empty. * * @return true if JSONArray is empty, otherwise false. */ public boolean isEmpty() { return this.myArrayList.isEmpty(); } /** * Add a collection's elements to the JSONArray. * * @param collection A Collection. * @param wrap {@code true} to call {@link JSONObject#wrap(Object)} for each item, {@code false} to add * the items directly * @param recursionDepth Variable for tracking the count of nested object creations. */ private void addAll(Collection collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size()); if (wrap) { for (Object o : collection) { this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration)); } } else { for (Object o : collection) { this.put(o); } } } /** * Add an Iterable's elements to the JSONArray. * * @param iter An Iterable. * @param wrap {@code true} to call {@link JSONObject#wrap(Object)} for each item, {@code false} to add the items * directly */ private void addAll(Iterable iter, boolean wrap) { if (wrap) { for (Object o : iter) { this.put(JSONObject.wrap(o)); } } else { for (Object o : iter) { this.put(o); } } } /** * Add an array's elements to the JSONArray. * * @param array Array. If the parameter passed is null, or not an array, JSONArray, Collection, or Iterable, an * exception will be thrown. * @param wrap {@code true} to call {@link JSONObject#wrap(Object)} for each item, {@code false} to add the items * directly * @throws JSONException If not an array or if an array value is non-finite number. */ private void addAll(Object array, boolean wrap) throws JSONException { this.addAll(array, wrap, 0); } /** * Add an array's elements to the JSONArray. * * @param array Array. If the parameter passed is null, or not an array, JSONArray, Collection, or * Iterable, an exception will be thrown. * @param wrap {@code true} to call {@link JSONObject#wrap(Object)} for each item, {@code false} to add * the items directly * @param recursionDepth Variable for tracking the count of nested object creations. */ private void addAll(Object array, boolean wrap, int recursionDepth) { addAll(array, wrap, recursionDepth, new JSONParserConfiguration()); } /** * Add an array's elements to the JSONArray. ` * * @param array Array. If the parameter passed is null, or not an array, JSONArray, Collection, or * Iterable, an exception will be thrown. * @param wrap {@code true} to call {@link JSONObject#wrap(Object)} for each item, {@code false} * to add the items directly * @param recursionDepth Variable for tracking the count of nested object creations. * @param jsonParserConfiguration Variable to pass parser custom configuration for json parsing. * @throws JSONException If not an array or if an array value is non-finite number. * @throws NullPointerException Thrown if the array parameter is null. */ private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException { if (array.getClass().isArray()) { int length = Array.getLength(array); this.myArrayList.ensureCapacity(this.myArrayList.size() + length); if (wrap) { for (int i = 0; i < length; i += 1) { this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration)); } } else { for (int i = 0; i < length; i += 1) { this.put(Array.get(array, i)); } } } else if (array instanceof JSONArray) { // use the built in array list `addAll` as all object // wrapping should have been completed in the original // JSONArray this.myArrayList.addAll(((JSONArray) array).myArrayList); } else if (array instanceof Collection) { this.addAll((Collection) array, wrap, recursionDepth); } else if (array instanceof Iterable) { this.addAll((Iterable) array, wrap); } else { throw new JSONException( "JSONArray initial value should be a string or collection or array."); } } /** * Create a new JSONException in a common format for incorrect conversions. * * @param idx index of the item * @param valueType the type of value being coerced to * @param cause optional cause of the coercion failure * @return JSONException that can be thrown. */ private static JSONException wrongValueFormatException( int idx, String valueType, Object value, Throwable cause) { if (value == null) { return new JSONException( "JSONArray[" + idx + "] is not a " + valueType + " (null)." , cause); } // don't try to toString collections or known object types that could be large. if (value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { return new JSONException( "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + ")." , cause); } return new JSONException( "JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ")." , cause); } }