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