Home | History | Annotate | Download | only in json
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.json;
     18 
     19 import java.util.ArrayList;
     20 import java.util.Collection;
     21 import java.util.Iterator;
     22 import java.util.LinkedHashMap;
     23 import java.util.Map;
     24 import java.util.Objects;
     25 import java.util.Set;
     26 
     27 // Note: this class was written without inspecting the non-free org.json sourcecode.
     28 
     29 /**
     30  * A modifiable set of name/value mappings. Names are unique, non-null strings.
     31  * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray
     32  * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}.
     33  * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link
     34  * Double#isInfinite() infinities}, or of any type not listed here.
     35  *
     36  * <p>This class can coerce values to another type when requested.
     37  * <ul>
     38  *   <li>When the requested type is a boolean, strings will be coerced using a
     39  *       case-insensitive comparison to "true" and "false".
     40  *   <li>When the requested type is a double, other {@link Number} types will
     41  *       be coerced using {@link Number#doubleValue() doubleValue}. Strings
     42  *       that can be coerced using {@link Double#valueOf(String)} will be.
     43  *   <li>When the requested type is an int, other {@link Number} types will
     44  *       be coerced using {@link Number#intValue() intValue}. Strings
     45  *       that can be coerced using {@link Double#valueOf(String)} will be,
     46  *       and then cast to int.
     47  *   <li><a name="lossy">When the requested type is a long, other {@link Number} types will
     48  *       be coerced using {@link Number#longValue() longValue}. Strings
     49  *       that can be coerced using {@link Double#valueOf(String)} will be,
     50  *       and then cast to long. This two-step conversion is lossy for very
     51  *       large values. For example, the string "9223372036854775806" yields the
     52  *       long 9223372036854775807.</a>
     53  *   <li>When the requested type is a String, other non-null values will be
     54  *       coerced using {@link String#valueOf(Object)}. Although null cannot be
     55  *       coerced, the sentinel value {@link JSONObject#NULL} is coerced to the
     56  *       string "null".
     57  * </ul>
     58  *
     59  * <p>This class can look up both mandatory and optional values:
     60  * <ul>
     61  *   <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This
     62  *       fails with a {@code JSONException} if the requested name has no value
     63  *       or if the value cannot be coerced to the requested type.
     64  *   <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This
     65  *       returns a system- or user-supplied default if the requested name has no
     66  *       value or if the value cannot be coerced to the requested type.
     67  * </ul>
     68  *
     69  * <p><strong>Warning:</strong> this class represents null in two incompatible
     70  * ways: the standard Java {@code null} reference, and the sentinel value {@link
     71  * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the
     72  * named entry from the object but {@code put(name, JSONObject.NULL)} stores an
     73  * entry whose value is {@code JSONObject.NULL}.
     74  *
     75  * <p>Instances of this class are not thread safe. Although this class is
     76  * nonfinal, it was not designed for inheritance and should not be subclassed.
     77  * In particular, self-use by overrideable methods is not specified. See
     78  * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
     79  * prohibit it" for further information.
     80  */
     81 public class JSONObject {
     82 
     83     private static final Double NEGATIVE_ZERO = -0d;
     84 
     85     /**
     86      * A sentinel value used to explicitly define a name with no value. Unlike
     87      * {@code null}, names with this value:
     88      * <ul>
     89      *   <li>show up in the {@link #names} array
     90      *   <li>show up in the {@link #keys} iterator
     91      *   <li>return {@code true} for {@link #has(String)}
     92      *   <li>do not throw on {@link #get(String)}
     93      *   <li>are included in the encoded JSON string.
     94      * </ul>
     95      *
     96      * <p>This value violates the general contract of {@link Object#equals} by
     97      * returning true when compared to {@code null}. Its {@link #toString}
     98      * method returns "null".
     99      */
    100     public static final Object NULL = new Object() {
    101         @Override public boolean equals(Object o) {
    102             return o == this || o == null; // API specifies this broken equals implementation
    103         }
    104         // at least make the broken equals(null) consistent with Objects.hashCode(null).
    105         @Override public int hashCode() { return Objects.hashCode(null); }
    106         @Override public String toString() {
    107             return "null";
    108         }
    109     };
    110 
    111     private final LinkedHashMap<String, Object> nameValuePairs;
    112 
    113     /**
    114      * Creates a {@code JSONObject} with no name/value mappings.
    115      */
    116     public JSONObject() {
    117         nameValuePairs = new LinkedHashMap<String, Object>();
    118     }
    119 
    120     /**
    121      * Creates a new {@code JSONObject} by copying all name/value mappings from
    122      * the given map.
    123      *
    124      * @param copyFrom a map whose keys are of type {@link String} and whose
    125      *     values are of supported types.
    126      * @throws NullPointerException if any of the map's keys are null.
    127      */
    128     /* (accept a raw type for API compatibility) */
    129     public JSONObject(Map copyFrom) {
    130         this();
    131         Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
    132         for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
    133             /*
    134              * Deviate from the original by checking that keys are non-null and
    135              * of the proper type. (We still defer validating the values).
    136              */
    137             String key = (String) entry.getKey();
    138             if (key == null) {
    139                 throw new NullPointerException("key == null");
    140             }
    141             nameValuePairs.put(key, wrap(entry.getValue()));
    142         }
    143     }
    144 
    145     /**
    146      * Creates a new {@code JSONObject} with name/value mappings from the next
    147      * object in the tokener.
    148      *
    149      * @param readFrom a tokener whose nextValue() method will yield a
    150      *     {@code JSONObject}.
    151      * @throws JSONException if the parse fails or doesn't yield a
    152      *     {@code JSONObject}.
    153      */
    154     public JSONObject(JSONTokener readFrom) throws JSONException {
    155         /*
    156          * Getting the parser to populate this could get tricky. Instead, just
    157          * parse to temporary JSONObject and then steal the data from that.
    158          */
    159         Object object = readFrom.nextValue();
    160         if (object instanceof JSONObject) {
    161             this.nameValuePairs = ((JSONObject) object).nameValuePairs;
    162         } else {
    163             throw JSON.typeMismatch(object, "JSONObject");
    164         }
    165     }
    166 
    167     /**
    168      * Creates a new {@code JSONObject} with name/value mappings from the JSON
    169      * string.
    170      *
    171      * @param json a JSON-encoded string containing an object.
    172      * @throws JSONException if the parse fails or doesn't yield a {@code
    173      *     JSONObject}.
    174      */
    175     public JSONObject(String json) throws JSONException {
    176         this(new JSONTokener(json));
    177     }
    178 
    179     /**
    180      * Creates a new {@code JSONObject} by copying mappings for the listed names
    181      * from the given object. Names that aren't present in {@code copyFrom} will
    182      * be skipped.
    183      */
    184     public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
    185         this();
    186         for (String name : names) {
    187             Object value = copyFrom.opt(name);
    188             if (value != null) {
    189                 nameValuePairs.put(name, value);
    190             }
    191         }
    192     }
    193 
    194     /**
    195      * Returns the number of name/value mappings in this object.
    196      */
    197     public int length() {
    198         return nameValuePairs.size();
    199     }
    200 
    201     /**
    202      * Maps {@code name} to {@code value}, clobbering any existing name/value
    203      * mapping with the same name.
    204      *
    205      * @return this object.
    206      */
    207     public JSONObject put(String name, boolean value) throws JSONException {
    208         nameValuePairs.put(checkName(name), value);
    209         return this;
    210     }
    211 
    212     /**
    213      * Maps {@code name} to {@code value}, clobbering any existing name/value
    214      * mapping with the same name.
    215      *
    216      * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
    217      *     {@link Double#isInfinite() infinities}.
    218      * @return this object.
    219      */
    220     public JSONObject put(String name, double value) throws JSONException {
    221         nameValuePairs.put(checkName(name), JSON.checkDouble(value));
    222         return this;
    223     }
    224 
    225     /**
    226      * Maps {@code name} to {@code value}, clobbering any existing name/value
    227      * mapping with the same name.
    228      *
    229      * @return this object.
    230      */
    231     public JSONObject put(String name, int value) throws JSONException {
    232         nameValuePairs.put(checkName(name), value);
    233         return this;
    234     }
    235 
    236     /**
    237      * Maps {@code name} to {@code value}, clobbering any existing name/value
    238      * mapping with the same name.
    239      *
    240      * @return this object.
    241      */
    242     public JSONObject put(String name, long value) throws JSONException {
    243         nameValuePairs.put(checkName(name), value);
    244         return this;
    245     }
    246 
    247     /**
    248      * Maps {@code name} to {@code value}, clobbering any existing name/value
    249      * mapping with the same name. If the value is {@code null}, any existing
    250      * mapping for {@code name} is removed.
    251      *
    252      * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    253      *     Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
    254      *     {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
    255      *     infinities}.
    256      * @return this object.
    257      */
    258     public JSONObject put(String name, Object value) throws JSONException {
    259         if (value == null) {
    260             nameValuePairs.remove(name);
    261             return this;
    262         }
    263         if (value instanceof Number) {
    264             // deviate from the original by checking all Numbers, not just floats & doubles
    265             JSON.checkDouble(((Number) value).doubleValue());
    266         }
    267         nameValuePairs.put(checkName(name), value);
    268         return this;
    269     }
    270 
    271     /**
    272      * Equivalent to {@code put(name, value)} when both parameters are non-null;
    273      * does nothing otherwise.
    274      */
    275     public JSONObject putOpt(String name, Object value) throws JSONException {
    276         if (name == null || value == null) {
    277             return this;
    278         }
    279         return put(name, value);
    280     }
    281 
    282     /**
    283      * Appends {@code value} to the array already mapped to {@code name}. If
    284      * this object has no mapping for {@code name}, this inserts a new mapping.
    285      * If the mapping exists but its value is not an array, the existing
    286      * and new values are inserted in order into a new array which is itself
    287      * mapped to {@code name}. In aggregate, this allows values to be added to a
    288      * mapping one at a time.
    289      *
    290      * <p> Note that {@code append(String, Object)} provides better semantics.
    291      * In particular, the mapping for {@code name} will <b>always</b> be a
    292      * {@link JSONArray}. Using {@code accumulate} will result in either a
    293      * {@link JSONArray} or a mapping whose type is the type of {@code value}
    294      * depending on the number of calls to it.
    295      *
    296      * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    297      *     Integer, Long, Double, {@link #NULL} or null. May not be {@link
    298      *     Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
    299      */
    300     // TODO: Change {@code append) to {@link #append} when append is
    301     // unhidden.
    302     public JSONObject accumulate(String name, Object value) throws JSONException {
    303         Object current = nameValuePairs.get(checkName(name));
    304         if (current == null) {
    305             return put(name, value);
    306         }
    307 
    308         if (current instanceof JSONArray) {
    309             JSONArray array = (JSONArray) current;
    310             array.checkedPut(value);
    311         } else {
    312             JSONArray array = new JSONArray();
    313             array.checkedPut(current);
    314             array.checkedPut(value);
    315             nameValuePairs.put(name, array);
    316         }
    317         return this;
    318     }
    319 
    320     /**
    321      * Appends values to the array mapped to {@code name}. A new {@link JSONArray}
    322      * mapping for {@code name} will be inserted if no mapping exists. If the existing
    323      * mapping for {@code name} is not a {@link JSONArray}, a {@link JSONException}
    324      * will be thrown.
    325      *
    326      * @throws JSONException if {@code name} is {@code null} or if the mapping for
    327      *         {@code name} is non-null and is not a {@link JSONArray}.
    328      *
    329      * @hide
    330      */
    331     public JSONObject append(String name, Object value) throws JSONException {
    332         Object current = nameValuePairs.get(checkName(name));
    333 
    334         final JSONArray array;
    335         if (current instanceof JSONArray) {
    336             array = (JSONArray) current;
    337         } else if (current == null) {
    338             JSONArray newArray = new JSONArray();
    339             nameValuePairs.put(name, newArray);
    340             array = newArray;
    341         } else {
    342             throw new JSONException("Key " + name + " is not a JSONArray");
    343         }
    344 
    345         array.checkedPut(value);
    346 
    347         return this;
    348     }
    349 
    350     String checkName(String name) throws JSONException {
    351         if (name == null) {
    352             throw new JSONException("Names must be non-null");
    353         }
    354         return name;
    355     }
    356 
    357     /**
    358      * Removes the named mapping if it exists; does nothing otherwise.
    359      *
    360      * @return the value previously mapped by {@code name}, or null if there was
    361      *     no such mapping.
    362      */
    363     public Object remove(String name) {
    364         return nameValuePairs.remove(name);
    365     }
    366 
    367     /**
    368      * Returns true if this object has no mapping for {@code name} or if it has
    369      * a mapping whose value is {@link #NULL}.
    370      */
    371     public boolean isNull(String name) {
    372         Object value = nameValuePairs.get(name);
    373         return value == null || value == NULL;
    374     }
    375 
    376     /**
    377      * Returns true if this object has a mapping for {@code name}. The mapping
    378      * may be {@link #NULL}.
    379      */
    380     public boolean has(String name) {
    381         return nameValuePairs.containsKey(name);
    382     }
    383 
    384     /**
    385      * Returns the value mapped by {@code name}, or throws if no such mapping exists.
    386      *
    387      * @throws JSONException if no such mapping exists.
    388      */
    389     public Object get(String name) throws JSONException {
    390         Object result = nameValuePairs.get(name);
    391         if (result == null) {
    392             throw new JSONException("No value for " + name);
    393         }
    394         return result;
    395     }
    396 
    397     /**
    398      * Returns the value mapped by {@code name}, or null if no such mapping
    399      * exists.
    400      */
    401     public Object opt(String name) {
    402         return nameValuePairs.get(name);
    403     }
    404 
    405     /**
    406      * Returns the value mapped by {@code name} if it exists and is a boolean or
    407      * can be coerced to a boolean, or throws otherwise.
    408      *
    409      * @throws JSONException if the mapping doesn't exist or cannot be coerced
    410      *     to a boolean.
    411      */
    412     public boolean getBoolean(String name) throws JSONException {
    413         Object object = get(name);
    414         Boolean result = JSON.toBoolean(object);
    415         if (result == null) {
    416             throw JSON.typeMismatch(name, object, "boolean");
    417         }
    418         return result;
    419     }
    420 
    421     /**
    422      * Returns the value mapped by {@code name} if it exists and is a boolean or
    423      * can be coerced to a boolean, or false otherwise.
    424      */
    425     public boolean optBoolean(String name) {
    426         return optBoolean(name, false);
    427     }
    428 
    429     /**
    430      * Returns the value mapped by {@code name} if it exists and is a boolean or
    431      * can be coerced to a boolean, or {@code fallback} otherwise.
    432      */
    433     public boolean optBoolean(String name, boolean fallback) {
    434         Object object = opt(name);
    435         Boolean result = JSON.toBoolean(object);
    436         return result != null ? result : fallback;
    437     }
    438 
    439     /**
    440      * Returns the value mapped by {@code name} if it exists and is a double or
    441      * can be coerced to a double, or throws otherwise.
    442      *
    443      * @throws JSONException if the mapping doesn't exist or cannot be coerced
    444      *     to a double.
    445      */
    446     public double getDouble(String name) throws JSONException {
    447         Object object = get(name);
    448         Double result = JSON.toDouble(object);
    449         if (result == null) {
    450             throw JSON.typeMismatch(name, object, "double");
    451         }
    452         return result;
    453     }
    454 
    455     /**
    456      * Returns the value mapped by {@code name} if it exists and is a double or
    457      * can be coerced to a double, or {@code NaN} otherwise.
    458      */
    459     public double optDouble(String name) {
    460         return optDouble(name, Double.NaN);
    461     }
    462 
    463     /**
    464      * Returns the value mapped by {@code name} if it exists and is a double or
    465      * can be coerced to a double, or {@code fallback} otherwise.
    466      */
    467     public double optDouble(String name, double fallback) {
    468         Object object = opt(name);
    469         Double result = JSON.toDouble(object);
    470         return result != null ? result : fallback;
    471     }
    472 
    473     /**
    474      * Returns the value mapped by {@code name} if it exists and is an int or
    475      * can be coerced to an int, or throws otherwise.
    476      *
    477      * @throws JSONException if the mapping doesn't exist or cannot be coerced
    478      *     to an int.
    479      */
    480     public int getInt(String name) throws JSONException {
    481         Object object = get(name);
    482         Integer result = JSON.toInteger(object);
    483         if (result == null) {
    484             throw JSON.typeMismatch(name, object, "int");
    485         }
    486         return result;
    487     }
    488 
    489     /**
    490      * Returns the value mapped by {@code name} if it exists and is an int or
    491      * can be coerced to an int, or 0 otherwise.
    492      */
    493     public int optInt(String name) {
    494         return optInt(name, 0);
    495     }
    496 
    497     /**
    498      * Returns the value mapped by {@code name} if it exists and is an int or
    499      * can be coerced to an int, or {@code fallback} otherwise.
    500      */
    501     public int optInt(String name, int fallback) {
    502         Object object = opt(name);
    503         Integer result = JSON.toInteger(object);
    504         return result != null ? result : fallback;
    505     }
    506 
    507     /**
    508      * Returns the value mapped by {@code name} if it exists and is a long or
    509      * can be coerced to a long, or throws otherwise.
    510      * Note that JSON represents numbers as doubles,
    511      * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON.
    512      *
    513      * @throws JSONException if the mapping doesn't exist or cannot be coerced
    514      *     to a long.
    515      */
    516     public long getLong(String name) throws JSONException {
    517         Object object = get(name);
    518         Long result = JSON.toLong(object);
    519         if (result == null) {
    520             throw JSON.typeMismatch(name, object, "long");
    521         }
    522         return result;
    523     }
    524 
    525     /**
    526      * Returns the value mapped by {@code name} if it exists and is a long or
    527      * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles,
    528      * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON.
    529      */
    530     public long optLong(String name) {
    531         return optLong(name, 0L);
    532     }
    533 
    534     /**
    535      * Returns the value mapped by {@code name} if it exists and is a long or
    536      * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents
    537      * numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer
    538      * numbers via JSON.
    539      */
    540     public long optLong(String name, long fallback) {
    541         Object object = opt(name);
    542         Long result = JSON.toLong(object);
    543         return result != null ? result : fallback;
    544     }
    545 
    546     /**
    547      * Returns the value mapped by {@code name} if it exists, coercing it if
    548      * necessary, or throws if no such mapping exists.
    549      *
    550      * @throws JSONException if no such mapping exists.
    551      */
    552     public String getString(String name) throws JSONException {
    553         Object object = get(name);
    554         String result = JSON.toString(object);
    555         if (result == null) {
    556             throw JSON.typeMismatch(name, object, "String");
    557         }
    558         return result;
    559     }
    560 
    561     /**
    562      * Returns the value mapped by {@code name} if it exists, coercing it if
    563      * necessary, or the empty string if no such mapping exists.
    564      */
    565     public String optString(String name) {
    566         return optString(name, "");
    567     }
    568 
    569     /**
    570      * Returns the value mapped by {@code name} if it exists, coercing it if
    571      * necessary, or {@code fallback} if no such mapping exists.
    572      */
    573     public String optString(String name, String fallback) {
    574         Object object = opt(name);
    575         String result = JSON.toString(object);
    576         return result != null ? result : fallback;
    577     }
    578 
    579     /**
    580      * Returns the value mapped by {@code name} if it exists and is a {@code
    581      * JSONArray}, or throws otherwise.
    582      *
    583      * @throws JSONException if the mapping doesn't exist or is not a {@code
    584      *     JSONArray}.
    585      */
    586     public JSONArray getJSONArray(String name) throws JSONException {
    587         Object object = get(name);
    588         if (object instanceof JSONArray) {
    589             return (JSONArray) object;
    590         } else {
    591             throw JSON.typeMismatch(name, object, "JSONArray");
    592         }
    593     }
    594 
    595     /**
    596      * Returns the value mapped by {@code name} if it exists and is a {@code
    597      * JSONArray}, or null otherwise.
    598      */
    599     public JSONArray optJSONArray(String name) {
    600         Object object = opt(name);
    601         return object instanceof JSONArray ? (JSONArray) object : null;
    602     }
    603 
    604     /**
    605      * Returns the value mapped by {@code name} if it exists and is a {@code
    606      * JSONObject}, or throws otherwise.
    607      *
    608      * @throws JSONException if the mapping doesn't exist or is not a {@code
    609      *     JSONObject}.
    610      */
    611     public JSONObject getJSONObject(String name) throws JSONException {
    612         Object object = get(name);
    613         if (object instanceof JSONObject) {
    614             return (JSONObject) object;
    615         } else {
    616             throw JSON.typeMismatch(name, object, "JSONObject");
    617         }
    618     }
    619 
    620     /**
    621      * Returns the value mapped by {@code name} if it exists and is a {@code
    622      * JSONObject}, or null otherwise.
    623      */
    624     public JSONObject optJSONObject(String name) {
    625         Object object = opt(name);
    626         return object instanceof JSONObject ? (JSONObject) object : null;
    627     }
    628 
    629     /**
    630      * Returns an array with the values corresponding to {@code names}. The
    631      * array contains null for names that aren't mapped. This method returns
    632      * null if {@code names} is either null or empty.
    633      */
    634     public JSONArray toJSONArray(JSONArray names) throws JSONException {
    635         JSONArray result = new JSONArray();
    636         if (names == null) {
    637             return null;
    638         }
    639         int length = names.length();
    640         if (length == 0) {
    641             return null;
    642         }
    643         for (int i = 0; i < length; i++) {
    644             String name = JSON.toString(names.opt(i));
    645             result.put(opt(name));
    646         }
    647         return result;
    648     }
    649 
    650     /**
    651      * Returns an iterator of the {@code String} names in this object. The
    652      * returned iterator supports {@link Iterator#remove() remove}, which will
    653      * remove the corresponding mapping from this object. If this object is
    654      * modified after the iterator is returned, the iterator's behavior is
    655      * undefined. The order of the keys is undefined.
    656      */
    657     public Iterator<String> keys() {
    658         return nameValuePairs.keySet().iterator();
    659     }
    660 
    661     /**
    662      * Returns the set of {@code String} names in this object. The returned set
    663      * is a view of the keys in this object. {@link Set#remove(Object)} will remove
    664      * the corresponding mapping from this object and set iterator behaviour
    665      * is undefined if this object is modified after it is returned.
    666      *
    667      * See {@link #keys()}.
    668      *
    669      * @hide.
    670      */
    671     public Set<String> keySet() {
    672         return nameValuePairs.keySet();
    673     }
    674 
    675     /**
    676      * Returns an array containing the string names in this object. This method
    677      * returns null if this object contains no mappings.
    678      */
    679     public JSONArray names() {
    680         return nameValuePairs.isEmpty()
    681                 ? null
    682                 : new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
    683     }
    684 
    685     /**
    686      * Encodes this object as a compact JSON string, such as:
    687      * <pre>{"query":"Pizza","locations":[94043,90210]}</pre>
    688      */
    689     @Override public String toString() {
    690         try {
    691             JSONStringer stringer = new JSONStringer();
    692             writeTo(stringer);
    693             return stringer.toString();
    694         } catch (JSONException e) {
    695             return null;
    696         }
    697     }
    698 
    699     /**
    700      * Encodes this object as a human readable JSON string for debugging, such
    701      * as:
    702      * <pre>
    703      * {
    704      *     "query": "Pizza",
    705      *     "locations": [
    706      *         94043,
    707      *         90210
    708      *     ]
    709      * }</pre>
    710      *
    711      * @param indentSpaces the number of spaces to indent for each level of
    712      *     nesting.
    713      */
    714     public String toString(int indentSpaces) throws JSONException {
    715         JSONStringer stringer = new JSONStringer(indentSpaces);
    716         writeTo(stringer);
    717         return stringer.toString();
    718     }
    719 
    720     void writeTo(JSONStringer stringer) throws JSONException {
    721         stringer.object();
    722         for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
    723             stringer.key(entry.getKey()).value(entry.getValue());
    724         }
    725         stringer.endObject();
    726     }
    727 
    728     /**
    729      * Encodes the number as a JSON string.
    730      *
    731      * @param number a finite value. May not be {@link Double#isNaN() NaNs} or
    732      *     {@link Double#isInfinite() infinities}.
    733      */
    734     public static String numberToString(Number number) throws JSONException {
    735         if (number == null) {
    736             throw new JSONException("Number must be non-null");
    737         }
    738 
    739         double doubleValue = number.doubleValue();
    740         JSON.checkDouble(doubleValue);
    741 
    742         // the original returns "-0" instead of "-0.0" for negative zero
    743         if (number.equals(NEGATIVE_ZERO)) {
    744             return "-0";
    745         }
    746 
    747         long longValue = number.longValue();
    748         if (doubleValue == (double) longValue) {
    749             return Long.toString(longValue);
    750         }
    751 
    752         return number.toString();
    753     }
    754 
    755     /**
    756      * Encodes {@code data} as a JSON string. This applies quotes and any
    757      * necessary character escaping.
    758      *
    759      * @param data the string to encode. Null will be interpreted as an empty
    760      *     string.
    761      */
    762     public static String quote(String data) {
    763         if (data == null) {
    764             return "\"\"";
    765         }
    766         try {
    767             JSONStringer stringer = new JSONStringer();
    768             stringer.open(JSONStringer.Scope.NULL, "");
    769             stringer.value(data);
    770             stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
    771             return stringer.toString();
    772         } catch (JSONException e) {
    773             throw new AssertionError();
    774         }
    775     }
    776 
    777     /**
    778      * Wraps the given object if necessary.
    779      *
    780      * <p>If the object is null or , returns {@link #NULL}.
    781      * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary.
    782      * If the object is {@code NULL}, no wrapping is necessary.
    783      * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}.
    784      * If the object is a {@code Map}, returns an equivalent {@code JSONObject}.
    785      * If the object is a primitive wrapper type or {@code String}, returns the object.
    786      * Otherwise if the object is from a {@code java} package, returns the result of {@code toString}.
    787      * If wrapping fails, returns null.
    788      */
    789     public static Object wrap(Object o) {
    790         if (o == null) {
    791             return NULL;
    792         }
    793         if (o instanceof JSONArray || o instanceof JSONObject) {
    794             return o;
    795         }
    796         if (o.equals(NULL)) {
    797             return o;
    798         }
    799         try {
    800             if (o instanceof Collection) {
    801                 return new JSONArray((Collection) o);
    802             } else if (o.getClass().isArray()) {
    803                 return new JSONArray(o);
    804             }
    805             if (o instanceof Map) {
    806                 return new JSONObject((Map) o);
    807             }
    808             if (o instanceof Boolean ||
    809                 o instanceof Byte ||
    810                 o instanceof Character ||
    811                 o instanceof Double ||
    812                 o instanceof Float ||
    813                 o instanceof Integer ||
    814                 o instanceof Long ||
    815                 o instanceof Short ||
    816                 o instanceof String) {
    817                 return o;
    818             }
    819             if (o.getClass().getPackage().getName().startsWith("java.")) {
    820                 return o.toString();
    821             }
    822         } catch (Exception ignored) {
    823         }
    824         return null;
    825     }
    826 }
    827