summaryrefslogtreecommitdiffstats
path: root/json
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2010-03-16 23:41:33 -0700
committerJesse Wilson <jessewilson@google.com>2010-03-17 11:17:47 -0700
commit1483c41ab440a713e2b935801792da9039ec5d61 (patch)
treedc95a9904622c2f6bde83a608db5b1649b8b1383 /json
parenta10509b68308e2bff501ece4862434108900c249 (diff)
downloadlibcore-1483c41ab440a713e2b935801792da9039ec5d61.zip
libcore-1483c41ab440a713e2b935801792da9039ec5d61.tar.gz
libcore-1483c41ab440a713e2b935801792da9039ec5d61.tar.bz2
Javadocs for JSONObject.
Change-Id: I5ec9df6a3a9baac8f4f498890cd35feff774737a
Diffstat (limited to 'json')
-rw-r--r--json/src/main/java/org/json/JSON.java4
-rw-r--r--json/src/main/java/org/json/JSONObject.java330
-rw-r--r--json/src/test/java/org/json/JSONObjectTest.java47
3 files changed, 374 insertions, 7 deletions
diff --git a/json/src/main/java/org/json/JSON.java b/json/src/main/java/org/json/JSON.java
index 029884b..b32124d 100644
--- a/json/src/main/java/org/json/JSON.java
+++ b/json/src/main/java/org/json/JSON.java
@@ -58,7 +58,7 @@ class JSON {
return ((Number) value).intValue();
} else if (value instanceof String) {
try {
- return Double.valueOf((String) value).intValue();
+ return (int) Double.parseDouble((String) value);
} catch (NumberFormatException e) {
}
}
@@ -72,7 +72,7 @@ class JSON {
return ((Number) value).longValue();
} else if (value instanceof String) {
try {
- return Double.valueOf((String) value).longValue();
+ return (long) Double.parseDouble((String) value);
} catch (NumberFormatException e) {
}
}
diff --git a/json/src/main/java/org/json/JSONObject.java b/json/src/main/java/org/json/JSONObject.java
index c92d549..fcc5d6a 100644
--- a/json/src/main/java/org/json/JSONObject.java
+++ b/json/src/main/java/org/json/JSONObject.java
@@ -24,14 +24,75 @@ import java.util.Map;
// Note: this class was written without inspecting the non-free org.json sourcecode.
/**
+ * A modifiable set of name/value mappings. Names are unique, non-null strings.
+ * Values may be other {@link JSONObject JSONObjects}, {@link JSONArray
+ * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}.
+ * Values may not be {@code null}, {@link Double#isNaN() NaNs} or {@link
+ * Double#isInfinite() infinities}.
*
+ * <p>This class can coerce values to another type when requested.
+ * <ul>
+ * <li>When the requested type is a boolean, strings will be coerced
+ * using {@link Boolean#valueOf(String)}.
+ * <li>When the requested type is a double, other {@link Number} types will
+ * be coerced using {@link Number#doubleValue() doubleValue()}. Strings
+ * that can be coerced using {@link Double#valueOf(String)} will be.
+ * <li>When the requested type is an int, other {@link Number} types will
+ * be coerced using {@link Number#intValue() intValue()}. Strings
+ * that can be coerced using {@link Double#valueOf(String)} will be,
+ * and then cast to int.
+ * <li>When the requested type is a long, other {@link Number} types will
+ * be coerced using {@link Number#longValue() longValue()}. Strings
+ * that can be coerced using {@link Double#valueOf(String)} will be,
+ * and then cast to long. This two-step conversion is lossy for very
+ * large values. For example, the string "9223372036854775806" yields the
+ * long 9223372036854775807.
+ * <li>When the requested type is a String, other non-null values will be
+ * coerced using {@link String#valueOf(Object)}.
+ * </ul>
*
- * <p>TODO: Note about self-use
+ * <p>This class can look up both mandatory and optional values:
+ * <ul>
+ * <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This
+ * fails with a {@code JSONException} if the requested name has no value
+ * or if the value cannot be coerced to the requested type.
+ * <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This
+ * returns a system- or user-supplied default if the requested name has no
+ * value or if the value cannot be coerced to the requested type.
+ * </ul>
+ *
+ * <p><strong>Warning:</strong> this class represents null in two incompatible
+ * ways: the standard Java {@code null} literal, and the sentinel value {@link
+ * JSONObject#NULL JSONObject.NULL}. In particular, calling {@code
+ * put(name, null)} removes the named entry from the object but {@code
+ * put(name, JSONObject.NULL)} stores an entry whose value is {@code
+ * JSONObject.NULL}.
+ *
+ * <p>Instances of this class are not thread safe. Although this class is
+ * nonfinal, it was not designed for inheritance and should not be subclassed.
+ * In particular, self-use by overridable methods is not specified. See
+ * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
+ * prohibit it" for further information.
*/
public class JSONObject {
private static final Double NEGATIVE_ZERO = -0d;
+ /**
+ * A sentinel value used to explicitly define a name with no value. Unlike
+ * {@code null}, names with this value:
+ * <ul>
+ * <li>show up in the {@link #names()} array
+ * <li>show up in the {@link #keys()} iterator
+ * <li>return {@code true} for {@link #has(String)}
+ * <li>do not throw on {@link #get(String)}
+ * <li>are included in the encoded JSON string.
+ * </ul>
+ *
+ * <p>This value violates the general contract of {@link Object#equals} by
+ * returning true when compared to {@code null}. Its {@link #toString}
+ * method returns "null".
+ */
public static final Object NULL = new Object() {
@Override public boolean equals(Object o) {
return o == this || o == null; // API specifies this broken equals implementation
@@ -43,11 +104,22 @@ public class JSONObject {
private final Map<String, Object> nameValuePairs;
+ /**
+ * Creates a JSONObject with no name/value mappings.
+ */
public JSONObject() {
nameValuePairs = new HashMap<String, Object>();
}
- /* Accept a raw type for API compatibility */
+ /**
+ * Creates a new JSONObject by copying all name/value mappings from the
+ * given map.
+ *
+ * @param copyFrom a map whose keys are of type {@link String} and whose
+ * values are of supported types. Values do not need to be homogeneous.
+ * @throws NullPointerException if any of the map's keys are null.
+ */
+ /* (accept a raw type for API compatibility) */
public JSONObject(Map copyFrom) {
this();
Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
@@ -64,6 +136,13 @@ public class JSONObject {
}
}
+ /**
+ * Creates a new JSONObject with name/value mappings from the next value in
+ * the tokenizer.
+ *
+ * @param readFrom a tokenizer whose nextValue() method will yield a JSONObject.
+ * @throws JSONException if the parse fails or doesn't yield a JSONObject.
+ */
public JSONObject(JSONTokener readFrom) throws JSONException {
/*
* Getting the parser to populate this could get tricky. Instead, just
@@ -77,10 +156,21 @@ public class JSONObject {
}
}
+ /**
+ * Creates a new JSONObject with name/value mappings from the JSON string.
+ *
+ * @param json a JSON-encoded string containing an object.
+ * @throws JSONException if the parse fails or doesn't yield a JSONObject.
+ */
public JSONObject(String json) throws JSONException {
this(new JSONTokener(json));
}
+ /**
+ * Creates a new JSONObject by copying mappings for the listed names from
+ * the given object. Names that aren't present in {@code copyFrom} will be
+ * skipped.
+ */
public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
this();
for (String name : names) {
@@ -91,30 +181,70 @@ public class JSONObject {
}
}
+ /**
+ * Returns the number of name/value mappings in this object.
+ */
public int length() {
return nameValuePairs.size();
}
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
public JSONObject put(String name, boolean value) throws JSONException {
nameValuePairs.put(checkName(name), value);
return this;
}
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this object.
+ */
public JSONObject put(String name, double value) throws JSONException {
nameValuePairs.put(checkName(name), JSON.checkDouble(value));
return this;
}
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
public JSONObject put(String name, int value) throws JSONException {
nameValuePairs.put(checkName(name), value);
return this;
}
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
public JSONObject put(String name, long value) throws JSONException {
nameValuePairs.put(checkName(name), value);
return this;
}
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name. If the value is {@code null}, any existing
+ * mapping for {@code name} is removed.
+ *
+ * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
+ * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
+ * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+ * infinities}.
+ * @return this object.
+ */
public JSONObject put(String name, Object value) throws JSONException {
if (value == null) {
nameValuePairs.remove(name);
@@ -128,6 +258,10 @@ public class JSONObject {
return this;
}
+ /**
+ * Equivalent to {@code put(name, value)} when both parameters are non-null;
+ * does nothing otherwise.
+ */
public JSONObject putOpt(String name, Object value) throws JSONException {
if (name == null || value == null) {
return this;
@@ -135,17 +269,36 @@ public class JSONObject {
return put(name, value);
}
+ /**
+ * Appends {@code value} to the array already mapped to {@code name}. If
+ * this object has no mapping for {@code name}, this inserts a new mapping.
+ * If the mapping exists but its value is not an array, the existing
+ * and new values are inserted in order into a new array which is itself
+ * mapped to {@code name}. In aggregate, this allows values to be added to a
+ * mapping one at a time.
+ *
+ * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
+ * Integer, Long, Double, {@link #NULL} or null. May not be {@link
+ * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
+ */
public JSONObject accumulate(String name, Object value) throws JSONException {
Object current = nameValuePairs.get(checkName(name));
if (current == null) {
- put(name, value);
- } else if (current instanceof JSONArray) {
+ return put(name, value);
+ }
+
+ // check in accumulate, since array.put(Object) doesn't do any checking
+ if (value instanceof Number) {
+ JSON.checkDouble(((Number) value).doubleValue());
+ }
+
+ if (current instanceof JSONArray) {
JSONArray array = (JSONArray) current;
array.put(value);
} else {
JSONArray array = new JSONArray();
array.put(current);
- array.put(value); // fails on bogus values
+ array.put(value);
nameValuePairs.put(name, array);
}
return this;
@@ -158,19 +311,38 @@ public class JSONObject {
return name;
}
+ /**
+ * Removes the named mapping if it exists; does nothing otherwise.
+ *
+ * @return the value previously mapped by {@code name}, or null if there was
+ * no such mapping.
+ */
public Object remove(String name) {
return nameValuePairs.remove(name);
}
+ /**
+ * Returns true if this object has no mapping for {@code name} or if it has
+ * a mapping whose value is {@link #NULL}.
+ */
public boolean isNull(String name) {
Object value = nameValuePairs.get(name);
return value == null || value == NULL;
}
+ /**
+ * Returns true if this object has a mapping for {@code name}. The mapping
+ * may be {@link #NULL}.
+ */
public boolean has(String name) {
return nameValuePairs.containsKey(name);
}
+ /**
+ * Returns the value mapped by {@code name}.
+ *
+ * @throws JSONException if no such mapping exists.
+ */
public Object get(String name) throws JSONException {
Object result = nameValuePairs.get(name);
if (result == null) {
@@ -179,10 +351,21 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name}, or null if no such mapping
+ * exists.
+ */
public Object opt(String name) {
return nameValuePairs.get(name);
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean.
+ *
+ * @throws JSONException if the mapping doesn't exist or cannot be coerced
+ * to a boolean.
+ */
public boolean getBoolean(String name) throws JSONException {
Object object = get(name);
Boolean result = JSON.toBoolean(object);
@@ -192,16 +375,31 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean. Returns false otherwise.
+ */
public boolean optBoolean(String name) {
return optBoolean(name, false);
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean. Returns {@code fallback} otherwise.
+ */
public boolean optBoolean(String name, boolean fallback) {
Object object = opt(name);
Boolean result = JSON.toBoolean(object);
return result != null ? result : fallback;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double.
+ *
+ * @throws JSONException if the mapping doesn't exist or cannot be coerced
+ * to a double.
+ */
public double getDouble(String name) throws JSONException {
Object object = get(name);
Double result = JSON.toDouble(object);
@@ -211,16 +409,31 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double. Returns {@code NaN} otherwise.
+ */
public double optDouble(String name) {
return optDouble(name, Double.NaN);
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double. Returns {@code fallback} otherwise.
+ */
public double optDouble(String name, double fallback) {
Object object = opt(name);
Double result = JSON.toDouble(object);
return result != null ? result : fallback;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int.
+ *
+ * @throws JSONException if the mapping doesn't exist or cannot be coerced
+ * to an int.
+ */
public int getInt(String name) throws JSONException {
Object object = get(name);
Integer result = JSON.toInteger(object);
@@ -230,16 +443,31 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int. Returns 0 otherwise.
+ */
public int optInt(String name) {
return optInt(name, 0);
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int. Returns {@code fallback} otherwise.
+ */
public int optInt(String name, int fallback) {
Object object = opt(name);
Integer result = JSON.toInteger(object);
return result != null ? result : fallback;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long.
+ *
+ * @throws JSONException if the mapping doesn't exist or cannot be coerced
+ * to a long.
+ */
public long getLong(String name) throws JSONException {
Object object = get(name);
Long result = JSON.toLong(object);
@@ -249,16 +477,30 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long. Returns 0 otherwise.
+ */
public long optLong(String name) {
return optLong(name, 0L);
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long. Returns {@code fallback} otherwise.
+ */
public long optLong(String name, long fallback) {
Object object = opt(name);
Long result = JSON.toLong(object);
return result != null ? result : fallback;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary.
+ *
+ * @throws JSONException if no such mapping exists.
+ */
public String getString(String name) throws JSONException {
Object object = get(name);
String result = JSON.toString(object);
@@ -268,16 +510,31 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary. Returns the empty string if no such mapping exists.
+ */
public String optString(String name) {
return optString(name, "");
}
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary. Returns {@code fallback} if no such mapping exists.
+ */
public String optString(String name, String fallback) {
Object object = opt(name);
String result = JSON.toString(object);
return result != null ? result : fallback;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a {@code
+ * JSONArray}.
+ *
+ * @throws JSONException if the mapping doesn't exist or is not a {@code
+ * JSONArray}.
+ */
public JSONArray getJSONArray(String name) throws JSONException {
Object object = get(name);
if (object instanceof JSONArray) {
@@ -287,11 +544,22 @@ public class JSONObject {
}
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a {@code
+ * JSONArray}. Returns null otherwise.
+ */
public JSONArray optJSONArray(String name) {
Object object = opt(name);
return object instanceof JSONArray ? (JSONArray) object : null;
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a {@code
+ * JSONObject}.
+ *
+ * @throws JSONException if the mapping doesn't exist or is not a {@code
+ * JSONObject}.
+ */
public JSONObject getJSONObject(String name) throws JSONException {
Object object = get(name);
if (object instanceof JSONObject) {
@@ -301,11 +569,20 @@ public class JSONObject {
}
}
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a {@code
+ * JSONObject}. Returns null otherwise.
+ */
public JSONObject optJSONObject(String name) {
Object object = opt(name);
return object instanceof JSONObject ? (JSONObject) object : null;
}
+ /**
+ * Returns an array with the values corresponding to {@code names}. The
+ * array contains null for names that aren't mapped. This method returns
+ * null if {@code names} is either null or empty.
+ */
public JSONArray toJSONArray(JSONArray names) throws JSONException {
JSONArray result = new JSONArray();
if (names == null) {
@@ -322,17 +599,32 @@ public class JSONObject {
return result;
}
+ /**
+ * Returns an iterator of the {@code String} names in this object. The
+ * returned iterator supports {@link Iterator#remove() remove()}, which will
+ * remove the corresponding mapping from this object. If this object is
+ * modified after the iterator is returned, the iterator's behavior is
+ * undefined. The order of the keys is undefined.
+ */
/* Return a raw type for API compatibility */
public Iterator keys() {
return nameValuePairs.keySet().iterator();
}
+ /**
+ * Returns an array containing the string names in this object. This method
+ * returns null if this object contains no mappings.
+ */
public JSONArray names() {
return nameValuePairs.isEmpty()
? null
: new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
}
+ /**
+ * Encodes this object as a compact JSON string, such as
+ * <pre>{"query":"Pizza","locations":[94043,90210]}</pre>
+ */
@Override public String toString() {
try {
JSONStringer stringer = new JSONStringer();
@@ -343,6 +635,21 @@ public class JSONObject {
}
}
+ /**
+ * Encodes this object as a human readable JSON string for debugging, such
+ * as
+ * <pre>
+ * {
+ * "query": "Pizza",
+ * "locations": [
+ * 94043,
+ * 90210
+ * ]
+ * }</pre>
+ *
+ * @param indentSpaces the number of spaces to indent for each level of
+ * nesting.
+ */
public String toString(int indentSpaces) throws JSONException {
JSONStringer stringer = new JSONStringer(indentSpaces);
writeTo(stringer);
@@ -357,6 +664,12 @@ public class JSONObject {
stringer.endObject();
}
+ /**
+ * Encodes the number as a JSON string.
+ *
+ * @param number a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ */
public static String numberToString(Number number) throws JSONException {
if (number == null) {
throw new JSONException("Number must be non-null");
@@ -378,6 +691,13 @@ public class JSONObject {
return number.toString();
}
+ /**
+ * Encodes {@code data} as a JSON string. This applies quotes and any
+ * necessary character escaping.
+ *
+ * @param data the string to encode. Null will be interpreted as an empty
+ * string.
+ */
public static String quote(String data) {
if (data == null) {
return "\"\"";
diff --git a/json/src/test/java/org/json/JSONObjectTest.java b/json/src/test/java/org/json/JSONObjectTest.java
index e431096..b896a7f 100644
--- a/json/src/test/java/org/json/JSONObjectTest.java
+++ b/json/src/test/java/org/json/JSONObjectTest.java
@@ -453,6 +453,53 @@ public class JSONObjectTest extends TestCase {
assertEquals(null, object.optJSONObject("foo"));
}
+ public void testNullCoercionToString() throws JSONException {
+ JSONObject object = new JSONObject();
+ object.put("foo", JSONObject.NULL);
+ assertEquals("null", object.getString("foo"));
+ }
+
+ public void testArrayCoercion() throws JSONException {
+ JSONObject object = new JSONObject();
+ object.put("foo", "[true]");
+ try {
+ object.getJSONArray("foo");
+ fail();
+ } catch (JSONException e) {
+ }
+ }
+
+ public void testObjectCoercion() throws JSONException {
+ JSONObject object = new JSONObject();
+ object.put("foo", "{}");
+ try {
+ object.getJSONObject("foo");
+ fail();
+ } catch (JSONException e) {
+ }
+ }
+
+ public void testAccumulateValueChecking() throws JSONException {
+ JSONObject object = new JSONObject();
+ try {
+ object.accumulate("foo", Double.NaN);
+ fail();
+ } catch (JSONException e) {
+ }
+ object.accumulate("foo", 1);
+ try {
+ object.accumulate("foo", Double.NaN);
+ fail();
+ } catch (JSONException e) {
+ }
+ object.accumulate("foo", 2);
+ try {
+ object.accumulate("foo", Double.NaN);
+ fail();
+ } catch (JSONException e) {
+ }
+ }
+
public void testToJSONArray() throws JSONException {
JSONObject object = new JSONObject();
Object value = new Object();