• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.polo.json;
2 
3 /*
4 Copyright (c) 2002 JSON.org
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 The Software shall be used for Good, not Evil.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 */
26 
27 import java.io.IOException;
28 import java.io.Writer;
29 import java.lang.reflect.Field;
30 import java.lang.reflect.Modifier;
31 import java.lang.reflect.Method;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.Map;
36 import java.util.TreeSet;
37 
38 /**
39  * A JSONObject is an unordered collection of name/value pairs. Its
40  * external form is a string wrapped in curly braces with colons between the
41  * names and values, and commas between the values and names. The internal form
42  * is an object having <code>get</code> and <code>opt</code> methods for
43  * accessing the values by name, and <code>put</code> methods for adding or
44  * replacing values by name. The values can be any of these types:
45  * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
46  * <code>Number</code>, <code>String</code>, or the <code>JSONObject.NULL</code>
47  * object. A JSONObject constructor can be used to convert an external form
48  * JSON text into an internal form whose values can be retrieved with the
49  * <code>get</code> and <code>opt</code> methods, or to convert values into a
50  * JSON text using the <code>put</code> and <code>toString</code> methods.
51  * A <code>get</code> method returns a value if one can be found, and throws an
52  * exception if one cannot be found. An <code>opt</code> method returns a
53  * default value instead of throwing an exception, and so is useful for
54  * obtaining optional values.
55  * <p>
56  * The generic <code>get()</code> and <code>opt()</code> methods return an
57  * object, which you can cast or query for type. There are also typed
58  * <code>get</code> and <code>opt</code> methods that do type checking and type
59  * coercion for you.
60  * <p>
61  * The <code>put</code> methods adds values to an object. For example, <pre>
62  *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
63  * produces the string <code>{"JSON": "Hello, World"}</code>.
64  * <p>
65  * The texts produced by the <code>toString</code> methods strictly conform to
66  * the JSON syntax rules.
67  * The constructors are more forgiving in the texts they will accept:
68  * <ul>
69  * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
70  *     before the closing brace.</li>
71  * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
72  *     quote)</small>.</li>
73  * <li>Strings do not need to be quoted at all if they do not begin with a quote
74  *     or single quote, and if they do not contain leading or trailing spaces,
75  *     and if they do not contain any of these characters:
76  *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
77  *     and if they are not the reserved words <code>true</code>,
78  *     <code>false</code>, or <code>null</code>.</li>
79  * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
80  *     by <code>:</code>.</li>
81  * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
82  *     well as by <code>,</code> <small>(comma)</small>.</li>
83  * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
84  *     <code>0x-</code> <small>(hex)</small> prefix.</li>
85  * </ul>
86  * @author JSON.org
87  * @version 2009-03-06
88  */
89 public class JSONObject {
90 
91     /**
92      * JSONObject.NULL is equivalent to the value that JavaScript calls null,
93      * whilst Java's null is equivalent to the value that JavaScript calls
94      * undefined.
95      */
96      private static final class Null {
97 
98         /**
99          * There is only intended to be a single instance of the NULL object,
100          * so the clone method returns itself.
101          * @return     NULL.
102          */
clone()103         protected final Object clone() {
104             return this;
105         }
106 
107 
108         /**
109          * A Null object is equal to the null value and to itself.
110          * @param object    An object to test for nullness.
111          * @return true if the object parameter is the JSONObject.NULL object
112          *  or null.
113          */
equals(Object object)114         public boolean equals(Object object) {
115             return object == null || object == this;
116         }
117 
118 
119         /**
120          * Get the "null" string value.
121          * @return The string "null".
122          */
toString()123         public String toString() {
124             return "null";
125         }
126     }
127 
128 
129     /**
130      * The map where the JSONObject's properties are kept.
131      */
132     private Map map;
133 
134 
135     /**
136      * It is sometimes more convenient and less ambiguous to have a
137      * <code>NULL</code> object than to use Java's <code>null</code> value.
138      * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
139      * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
140      */
141     public static final Object NULL = new Null();
142 
143 
144     /**
145      * Construct an empty JSONObject.
146      */
JSONObject()147     public JSONObject() {
148         this.map = new HashMap();
149     }
150 
151 
152     /**
153      * Construct a JSONObject from a subset of another JSONObject.
154      * An array of strings is used to identify the keys that should be copied.
155      * Missing keys are ignored.
156      * @param jo A JSONObject.
157      * @param names An array of strings.
158      * @exception JSONException If a value is a non-finite number or if a name is duplicated.
159      */
JSONObject(JSONObject jo, String[] names)160     public JSONObject(JSONObject jo, String[] names) throws JSONException {
161         this();
162         for (int i = 0; i < names.length; i += 1) {
163             putOnce(names[i], jo.opt(names[i]));
164         }
165     }
166 
167 
168     /**
169      * Construct a JSONObject from a JSONTokener.
170      * @param x A JSONTokener object containing the source string.
171      * @throws JSONException If there is a syntax error in the source string
172      *  or a duplicated key.
173      */
JSONObject(JSONTokener x)174     public JSONObject(JSONTokener x) throws JSONException {
175         this();
176         char c;
177         String key;
178 
179         if (x.nextClean() != '{') {
180             throw x.syntaxError("A JSONObject text must begin with '{'");
181         }
182         for (;;) {
183             c = x.nextClean();
184             switch (c) {
185             case 0:
186                 throw x.syntaxError("A JSONObject text must end with '}'");
187             case '}':
188                 return;
189             default:
190                 x.back();
191                 key = x.nextValue().toString();
192             }
193 
194             /*
195              * The key is followed by ':'. We will also tolerate '=' or '=>'.
196              */
197 
198             c = x.nextClean();
199             if (c == '=') {
200                 if (x.next() != '>') {
201                     x.back();
202                 }
203             } else if (c != ':') {
204                 throw x.syntaxError("Expected a ':' after a key");
205             }
206             putOnce(key, x.nextValue());
207 
208             /*
209              * Pairs are separated by ','. We will also tolerate ';'.
210              */
211 
212             switch (x.nextClean()) {
213             case ';':
214             case ',':
215                 if (x.nextClean() == '}') {
216                     return;
217                 }
218                 x.back();
219                 break;
220             case '}':
221                 return;
222             default:
223                 throw x.syntaxError("Expected a ',' or '}'");
224             }
225         }
226     }
227 
228 
229     /**
230      * Construct a JSONObject from a Map.
231      *
232      * @param map A map object that can be used to initialize the contents of
233      *  the JSONObject.
234      */
JSONObject(Map map)235     public JSONObject(Map map) {
236         this.map = (map == null) ? new HashMap() : map;
237     }
238 
239 
240     /**
241      * Construct a JSONObject from a Map.
242      *
243      * Note: Use this constructor when the map contains <key,bean>.
244      *
245      * @param map - A map with Key-Bean data.
246      * @param includeSuperClass - Tell whether to include the super class properties.
247      */
JSONObject(Map map, boolean includeSuperClass)248     public JSONObject(Map map, boolean includeSuperClass) {
249         this.map = new HashMap();
250         if (map != null) {
251             Iterator i = map.entrySet().iterator();
252             while (i.hasNext()) {
253                 Map.Entry e = (Map.Entry)i.next();
254                 if (isStandardProperty(e.getValue().getClass())) {
255                     this.map.put(e.getKey(), e.getValue());
256                 } else {
257                     this.map.put(e.getKey(), new JSONObject(e.getValue(),
258                             includeSuperClass));
259                 }
260             }
261         }
262     }
263 
264 
265     /**
266      * Construct a JSONObject from an Object using bean getters.
267      * It reflects on all of the public methods of the object.
268      * For each of the methods with no parameters and a name starting
269      * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
270      * the method is invoked, and a key and the value returned from the getter method
271      * are put into the new JSONObject.
272      *
273      * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
274      * If the second remaining character is not upper case, then the first
275      * character is converted to lower case.
276      *
277      * For example, if an object has a method named <code>"getName"</code>, and
278      * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
279      * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
280      *
281      * @param bean An object that has getter methods that should be used
282      * to make a JSONObject.
283      */
JSONObject(Object bean)284     public JSONObject(Object bean) {
285         this();
286         populateInternalMap(bean, false);
287     }
288 
289 
290     /**
291      * Construct a JSONObject from an Object using bean getters.
292      * It reflects on all of the public methods of the object.
293      * For each of the methods with no parameters and a name starting
294      * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
295      * the method is invoked, and a key and the value returned from the getter method
296      * are put into the new JSONObject.
297      *
298      * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
299      * If the second remaining character is not upper case, then the first
300      * character is converted to lower case.
301      *
302      * @param bean An object that has getter methods that should be used
303      * to make a JSONObject.
304      * @param includeSuperClass If true, include the super class properties.
305      */
JSONObject(Object bean, boolean includeSuperClass)306     public JSONObject(Object bean, boolean includeSuperClass) {
307         this();
308         populateInternalMap(bean, includeSuperClass);
309     }
310 
populateInternalMap(Object bean, boolean includeSuperClass)311     private void populateInternalMap(Object bean, boolean includeSuperClass){
312         Class klass = bean.getClass();
313 
314         /* If klass.getSuperClass is System class then force includeSuperClass to false. */
315 
316         if (klass.getClassLoader() == null) {
317             includeSuperClass = false;
318         }
319 
320         Method[] methods = (includeSuperClass) ?
321                 klass.getMethods() : klass.getDeclaredMethods();
322         for (int i = 0; i < methods.length; i += 1) {
323             try {
324                 Method method = methods[i];
325                 if (Modifier.isPublic(method.getModifiers())) {
326                     String name = method.getName();
327                     String key = "";
328                     if (name.startsWith("get")) {
329                         key = name.substring(3);
330                     } else if (name.startsWith("is")) {
331                         key = name.substring(2);
332                     }
333                     if (key.length() > 0 &&
334                             Character.isUpperCase(key.charAt(0)) &&
335                             method.getParameterTypes().length == 0) {
336                         if (key.length() == 1) {
337                             key = key.toLowerCase();
338                         } else if (!Character.isUpperCase(key.charAt(1))) {
339                             key = key.substring(0, 1).toLowerCase() +
340                                 key.substring(1);
341                         }
342 
343                         Object result = method.invoke(bean, (Object[])null);
344                         if (result == null) {
345                             map.put(key, NULL);
346                         } else if (result.getClass().isArray()) {
347                             map.put(key, new JSONArray(result, includeSuperClass));
348                         } else if (result instanceof Collection) { // List or Set
349                             map.put(key, new JSONArray((Collection)result, includeSuperClass));
350                         } else if (result instanceof Map) {
351                             map.put(key, new JSONObject((Map)result, includeSuperClass));
352                         } else if (isStandardProperty(result.getClass())) { // Primitives, String and Wrapper
353                             map.put(key, result);
354                         } else {
355                             if (result.getClass().getPackage().getName().startsWith("java") ||
356                                     result.getClass().getClassLoader() == null) {
357                                 map.put(key, result.toString());
358                             } else { // User defined Objects
359                                 map.put(key, new JSONObject(result, includeSuperClass));
360                             }
361                         }
362                     }
363                 }
364             } catch (Exception e) {
365                 throw new RuntimeException(e);
366             }
367         }
368     }
369 
370 
isStandardProperty(Class clazz)371     static boolean isStandardProperty(Class clazz) {
372         return clazz.isPrimitive()                  ||
373             clazz.isAssignableFrom(Byte.class)      ||
374             clazz.isAssignableFrom(Short.class)     ||
375             clazz.isAssignableFrom(Integer.class)   ||
376             clazz.isAssignableFrom(Long.class)      ||
377             clazz.isAssignableFrom(Float.class)     ||
378             clazz.isAssignableFrom(Double.class)    ||
379             clazz.isAssignableFrom(Character.class) ||
380             clazz.isAssignableFrom(String.class)    ||
381             clazz.isAssignableFrom(Boolean.class);
382     }
383 
384 
385     /**
386      * Construct a JSONObject from an Object, using reflection to find the
387      * public members. The resulting JSONObject's keys will be the strings
388      * from the names array, and the values will be the field values associated
389      * with those keys in the object. If a key is not found or not visible,
390      * then it will not be copied into the new JSONObject.
391      * @param object An object that has fields that should be used to make a
392      * JSONObject.
393      * @param names An array of strings, the names of the fields to be obtained
394      * from the object.
395      */
JSONObject(Object object, String names[])396     public JSONObject(Object object, String names[]) {
397         this();
398         Class c = object.getClass();
399         for (int i = 0; i < names.length; i += 1) {
400             String name = names[i];
401             try {
402                 putOpt(name, c.getField(name).get(object));
403             } catch (Exception e) {
404                 /* forget about it */
405             }
406         }
407     }
408 
409 
410     /**
411      * Construct a JSONObject from a source JSON text string.
412      * This is the most commonly used JSONObject constructor.
413      * @param source    A string beginning
414      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
415      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
416      * @exception JSONException If there is a syntax error in the source
417      *  string or a duplicated key.
418      */
JSONObject(String source)419     public JSONObject(String source) throws JSONException {
420         this(new JSONTokener(source));
421     }
422 
423 
424     /**
425      * Accumulate values under a key. It is similar to the put method except
426      * that if there is already an object stored under the key then a
427      * JSONArray is stored under the key to hold all of the accumulated values.
428      * If there is already a JSONArray, then the new value is appended to it.
429      * In contrast, the put method replaces the previous value.
430      * @param key   A key string.
431      * @param value An object to be accumulated under the key.
432      * @return this.
433      * @throws JSONException If the value is an invalid number
434      *  or if the key is null.
435      */
accumulate(String key, Object value)436     public JSONObject accumulate(String key, Object value)
437             throws JSONException {
438         testValidity(value);
439         Object o = opt(key);
440         if (o == null) {
441             put(key, value instanceof JSONArray ?
442                     new JSONArray().put(value) :
443                     value);
444         } else if (o instanceof JSONArray) {
445             ((JSONArray)o).put(value);
446         } else {
447             put(key, new JSONArray().put(o).put(value));
448         }
449         return this;
450     }
451 
452 
453     /**
454      * Append values to the array under a key. If the key does not exist in the
455      * JSONObject, then the key is put in the JSONObject with its value being a
456      * JSONArray containing the value parameter. If the key was already
457      * associated with a JSONArray, then the value parameter is appended to it.
458      * @param key   A key string.
459      * @param value An object to be accumulated under the key.
460      * @return this.
461      * @throws JSONException If the key is null or if the current value
462      *  associated with the key is not a JSONArray.
463      */
append(String key, Object value)464     public JSONObject append(String key, Object value)
465             throws JSONException {
466         testValidity(value);
467         Object o = opt(key);
468         if (o == null) {
469             put(key, new JSONArray().put(value));
470         } else if (o instanceof JSONArray) {
471             put(key, ((JSONArray)o).put(value));
472         } else {
473             throw new JSONException("JSONObject[" + key +
474                     "] is not a JSONArray.");
475         }
476         return this;
477     }
478 
479 
480     /**
481      * Produce a string from a double. The string "null" will be returned if
482      * the number is not finite.
483      * @param  d A double.
484      * @return A String.
485      */
doubleToString(double d)486     static public String doubleToString(double d) {
487         if (Double.isInfinite(d) || Double.isNaN(d)) {
488             return "null";
489         }
490 
491 // Shave off trailing zeros and decimal point, if possible.
492 
493         String s = Double.toString(d);
494         if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
495             while (s.endsWith("0")) {
496                 s = s.substring(0, s.length() - 1);
497             }
498             if (s.endsWith(".")) {
499                 s = s.substring(0, s.length() - 1);
500             }
501         }
502         return s;
503     }
504 
505 
506     /**
507      * Get the value object associated with a key.
508      *
509      * @param key   A key string.
510      * @return      The object associated with the key.
511      * @throws   JSONException if the key is not found.
512      */
get(String key)513     public Object get(String key) throws JSONException {
514         Object o = opt(key);
515         if (o == null) {
516             throw new JSONException("JSONObject[" + quote(key) +
517                     "] not found.");
518         }
519         return o;
520     }
521 
522 
523     /**
524      * Get the boolean value associated with a key.
525      *
526      * @param key   A key string.
527      * @return      The truth.
528      * @throws   JSONException
529      *  if the value is not a Boolean or the String "true" or "false".
530      */
getBoolean(String key)531     public boolean getBoolean(String key) throws JSONException {
532         Object o = get(key);
533         if (o.equals(Boolean.FALSE) ||
534                 (o instanceof String &&
535                 ((String)o).equalsIgnoreCase("false"))) {
536             return false;
537         } else if (o.equals(Boolean.TRUE) ||
538                 (o instanceof String &&
539                 ((String)o).equalsIgnoreCase("true"))) {
540             return true;
541         }
542         throw new JSONException("JSONObject[" + quote(key) +
543                 "] is not a Boolean.");
544     }
545 
546 
547     /**
548      * Get the double value associated with a key.
549      * @param key   A key string.
550      * @return      The numeric value.
551      * @throws JSONException if the key is not found or
552      *  if the value is not a Number object and cannot be converted to a number.
553      */
getDouble(String key)554     public double getDouble(String key) throws JSONException {
555         Object o = get(key);
556         try {
557             return o instanceof Number ?
558                 ((Number)o).doubleValue() :
559                 Double.valueOf((String)o).doubleValue();
560         } catch (Exception e) {
561             throw new JSONException("JSONObject[" + quote(key) +
562                 "] is not a number.");
563         }
564     }
565 
566 
567     /**
568      * Get the int value associated with a key. If the number value is too
569      * large for an int, it will be clipped.
570      *
571      * @param key   A key string.
572      * @return      The integer value.
573      * @throws   JSONException if the key is not found or if the value cannot
574      *  be converted to an integer.
575      */
getInt(String key)576     public int getInt(String key) throws JSONException {
577         Object o = get(key);
578         return o instanceof Number ?
579                 ((Number)o).intValue() : (int)getDouble(key);
580     }
581 
582 
583     /**
584      * Get the JSONArray value associated with a key.
585      *
586      * @param key   A key string.
587      * @return      A JSONArray which is the value.
588      * @throws   JSONException if the key is not found or
589      *  if the value is not a JSONArray.
590      */
getJSONArray(String key)591     public JSONArray getJSONArray(String key) throws JSONException {
592         Object o = get(key);
593         if (o instanceof JSONArray) {
594             return (JSONArray)o;
595         }
596         throw new JSONException("JSONObject[" + quote(key) +
597                 "] is not a JSONArray.");
598     }
599 
600 
601     /**
602      * Get the JSONObject value associated with a key.
603      *
604      * @param key   A key string.
605      * @return      A JSONObject which is the value.
606      * @throws   JSONException if the key is not found or
607      *  if the value is not a JSONObject.
608      */
getJSONObject(String key)609     public JSONObject getJSONObject(String key) throws JSONException {
610         Object o = get(key);
611         if (o instanceof JSONObject) {
612             return (JSONObject)o;
613         }
614         throw new JSONException("JSONObject[" + quote(key) +
615                 "] is not a JSONObject.");
616     }
617 
618 
619     /**
620      * Get the long value associated with a key. If the number value is too
621      * long for a long, it will be clipped.
622      *
623      * @param key   A key string.
624      * @return      The long value.
625      * @throws   JSONException if the key is not found or if the value cannot
626      *  be converted to a long.
627      */
getLong(String key)628     public long getLong(String key) throws JSONException {
629         Object o = get(key);
630         return o instanceof Number ?
631                 ((Number)o).longValue() : (long)getDouble(key);
632     }
633 
634 
635     /**
636      * Get an array of field names from a JSONObject.
637      *
638      * @return An array of field names, or null if there are no names.
639      */
getNames(JSONObject jo)640     public static String[] getNames(JSONObject jo) {
641         int length = jo.length();
642         if (length == 0) {
643             return null;
644         }
645         Iterator i = jo.keys();
646         String[] names = new String[length];
647         int j = 0;
648         while (i.hasNext()) {
649             names[j] = (String)i.next();
650             j += 1;
651         }
652         return names;
653     }
654 
655 
656     /**
657      * Get an array of field names from an Object.
658      *
659      * @return An array of field names, or null if there are no names.
660      */
getNames(Object object)661     public static String[] getNames(Object object) {
662         if (object == null) {
663             return null;
664         }
665         Class klass = object.getClass();
666         Field[] fields = klass.getFields();
667         int length = fields.length;
668         if (length == 0) {
669             return null;
670         }
671         String[] names = new String[length];
672         for (int i = 0; i < length; i += 1) {
673             names[i] = fields[i].getName();
674         }
675         return names;
676     }
677 
678 
679     /**
680      * Get the string associated with a key.
681      *
682      * @param key   A key string.
683      * @return      A string which is the value.
684      * @throws   JSONException if the key is not found.
685      */
getString(String key)686     public String getString(String key) throws JSONException {
687         return get(key).toString();
688     }
689 
690 
691     /**
692      * Determine if the JSONObject contains a specific key.
693      * @param key   A key string.
694      * @return      true if the key exists in the JSONObject.
695      */
has(String key)696     public boolean has(String key) {
697         return this.map.containsKey(key);
698     }
699 
700 
701     /**
702      * Determine if the value associated with the key is null or if there is
703      *  no value.
704      * @param key   A key string.
705      * @return      true if there is no value associated with the key or if
706      *  the value is the JSONObject.NULL object.
707      */
isNull(String key)708     public boolean isNull(String key) {
709         return JSONObject.NULL.equals(opt(key));
710     }
711 
712 
713     /**
714      * Get an enumeration of the keys of the JSONObject.
715      *
716      * @return An iterator of the keys.
717      */
keys()718     public Iterator keys() {
719         return this.map.keySet().iterator();
720     }
721 
722 
723     /**
724      * Get the number of keys stored in the JSONObject.
725      *
726      * @return The number of keys in the JSONObject.
727      */
length()728     public int length() {
729         return this.map.size();
730     }
731 
732 
733     /**
734      * Produce a JSONArray containing the names of the elements of this
735      * JSONObject.
736      * @return A JSONArray containing the key strings, or null if the JSONObject
737      * is empty.
738      */
names()739     public JSONArray names() {
740         JSONArray ja = new JSONArray();
741         Iterator  keys = keys();
742         while (keys.hasNext()) {
743             ja.put(keys.next());
744         }
745         return ja.length() == 0 ? null : ja;
746     }
747 
748     /**
749      * Produce a string from a Number.
750      * @param  n A Number
751      * @return A String.
752      * @throws JSONException If n is a non-finite number.
753      */
numberToString(Number n)754     static public String numberToString(Number n)
755             throws JSONException {
756         if (n == null) {
757             throw new JSONException("Null pointer");
758         }
759         testValidity(n);
760 
761 // Shave off trailing zeros and decimal point, if possible.
762 
763         String s = n.toString();
764         if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
765             while (s.endsWith("0")) {
766                 s = s.substring(0, s.length() - 1);
767             }
768             if (s.endsWith(".")) {
769                 s = s.substring(0, s.length() - 1);
770             }
771         }
772         return s;
773     }
774 
775 
776     /**
777      * Get an optional value associated with a key.
778      * @param key   A key string.
779      * @return      An object which is the value, or null if there is no value.
780      */
opt(String key)781     public Object opt(String key) {
782         return key == null ? null : this.map.get(key);
783     }
784 
785 
786     /**
787      * Get an optional boolean associated with a key.
788      * It returns false if there is no such key, or if the value is not
789      * Boolean.TRUE or the String "true".
790      *
791      * @param key   A key string.
792      * @return      The truth.
793      */
optBoolean(String key)794     public boolean optBoolean(String key) {
795         return optBoolean(key, false);
796     }
797 
798 
799     /**
800      * Get an optional boolean associated with a key.
801      * It returns the defaultValue if there is no such key, or if it is not
802      * a Boolean or the String "true" or "false" (case insensitive).
803      *
804      * @param key              A key string.
805      * @param defaultValue     The default.
806      * @return      The truth.
807      */
optBoolean(String key, boolean defaultValue)808     public boolean optBoolean(String key, boolean defaultValue) {
809         try {
810             return getBoolean(key);
811         } catch (Exception e) {
812             return defaultValue;
813         }
814     }
815 
816 
817     /**
818      * Put a key/value pair in the JSONObject, where the value will be a
819      * JSONArray which is produced from a Collection.
820      * @param key   A key string.
821      * @param value A Collection value.
822      * @return      this.
823      * @throws JSONException
824      */
put(String key, Collection value)825     public JSONObject put(String key, Collection value) throws JSONException {
826         put(key, new JSONArray(value));
827         return this;
828     }
829 
830 
831     /**
832      * Get an optional double associated with a key,
833      * or NaN if there is no such key or if its value is not a number.
834      * If the value is a string, an attempt will be made to evaluate it as
835      * a number.
836      *
837      * @param key   A string which is the key.
838      * @return      An object which is the value.
839      */
optDouble(String key)840     public double optDouble(String key) {
841         return optDouble(key, Double.NaN);
842     }
843 
844 
845     /**
846      * Get an optional double associated with a key, or the
847      * defaultValue if there is no such key or if its value is not a number.
848      * If the value is a string, an attempt will be made to evaluate it as
849      * a number.
850      *
851      * @param key   A key string.
852      * @param defaultValue     The default.
853      * @return      An object which is the value.
854      */
optDouble(String key, double defaultValue)855     public double optDouble(String key, double defaultValue) {
856         try {
857             Object o = opt(key);
858             return o instanceof Number ? ((Number)o).doubleValue() :
859                 new Double((String)o).doubleValue();
860         } catch (Exception e) {
861             return defaultValue;
862         }
863     }
864 
865 
866     /**
867      * Get an optional int value associated with a key,
868      * or zero if there is no such key or if the value is not a number.
869      * If the value is a string, an attempt will be made to evaluate it as
870      * a number.
871      *
872      * @param key   A key string.
873      * @return      An object which is the value.
874      */
optInt(String key)875     public int optInt(String key) {
876         return optInt(key, 0);
877     }
878 
879 
880     /**
881      * Get an optional int value associated with a key,
882      * or the default if there is no such key or if the value is not a number.
883      * If the value is a string, an attempt will be made to evaluate it as
884      * a number.
885      *
886      * @param key   A key string.
887      * @param defaultValue     The default.
888      * @return      An object which is the value.
889      */
optInt(String key, int defaultValue)890     public int optInt(String key, int defaultValue) {
891         try {
892             return getInt(key);
893         } catch (Exception e) {
894             return defaultValue;
895         }
896     }
897 
898 
899     /**
900      * Get an optional JSONArray associated with a key.
901      * It returns null if there is no such key, or if its value is not a
902      * JSONArray.
903      *
904      * @param key   A key string.
905      * @return      A JSONArray which is the value.
906      */
optJSONArray(String key)907     public JSONArray optJSONArray(String key) {
908         Object o = opt(key);
909         return o instanceof JSONArray ? (JSONArray)o : null;
910     }
911 
912 
913     /**
914      * Get an optional JSONObject associated with a key.
915      * It returns null if there is no such key, or if its value is not a
916      * JSONObject.
917      *
918      * @param key   A key string.
919      * @return      A JSONObject which is the value.
920      */
optJSONObject(String key)921     public JSONObject optJSONObject(String key) {
922         Object o = opt(key);
923         return o instanceof JSONObject ? (JSONObject)o : null;
924     }
925 
926 
927     /**
928      * Get an optional long value associated with a key,
929      * or zero if there is no such key or if the value is not a number.
930      * If the value is a string, an attempt will be made to evaluate it as
931      * a number.
932      *
933      * @param key   A key string.
934      * @return      An object which is the value.
935      */
optLong(String key)936     public long optLong(String key) {
937         return optLong(key, 0);
938     }
939 
940 
941     /**
942      * Get an optional long value associated with a key,
943      * or the default if there is no such key or if the value is not a number.
944      * If the value is a string, an attempt will be made to evaluate it as
945      * a number.
946      *
947      * @param key   A key string.
948      * @param defaultValue     The default.
949      * @return      An object which is the value.
950      */
optLong(String key, long defaultValue)951     public long optLong(String key, long defaultValue) {
952         try {
953             return getLong(key);
954         } catch (Exception e) {
955             return defaultValue;
956         }
957     }
958 
959 
960     /**
961      * Get an optional string associated with a key.
962      * It returns an empty string if there is no such key. If the value is not
963      * a string and is not null, then it is coverted to a string.
964      *
965      * @param key   A key string.
966      * @return      A string which is the value.
967      */
optString(String key)968     public String optString(String key) {
969         return optString(key, "");
970     }
971 
972 
973     /**
974      * Get an optional string associated with a key.
975      * It returns the defaultValue if there is no such key.
976      *
977      * @param key   A key string.
978      * @param defaultValue     The default.
979      * @return      A string which is the value.
980      */
optString(String key, String defaultValue)981     public String optString(String key, String defaultValue) {
982         Object o = opt(key);
983         return o != null ? o.toString() : defaultValue;
984     }
985 
986 
987     /**
988      * Put a key/boolean pair in the JSONObject.
989      *
990      * @param key   A key string.
991      * @param value A boolean which is the value.
992      * @return this.
993      * @throws JSONException If the key is null.
994      */
put(String key, boolean value)995     public JSONObject put(String key, boolean value) throws JSONException {
996         put(key, value ? Boolean.TRUE : Boolean.FALSE);
997         return this;
998     }
999 
1000 
1001     /**
1002      * Put a key/double pair in the JSONObject.
1003      *
1004      * @param key   A key string.
1005      * @param value A double which is the value.
1006      * @return this.
1007      * @throws JSONException If the key is null or if the number is invalid.
1008      */
put(String key, double value)1009     public JSONObject put(String key, double value) throws JSONException {
1010         put(key, new Double(value));
1011         return this;
1012     }
1013 
1014 
1015     /**
1016      * Put a key/int pair in the JSONObject.
1017      *
1018      * @param key   A key string.
1019      * @param value An int which is the value.
1020      * @return this.
1021      * @throws JSONException If the key is null.
1022      */
put(String key, int value)1023     public JSONObject put(String key, int value) throws JSONException {
1024         put(key, new Integer(value));
1025         return this;
1026     }
1027 
1028 
1029     /**
1030      * Put a key/long pair in the JSONObject.
1031      *
1032      * @param key   A key string.
1033      * @param value A long which is the value.
1034      * @return this.
1035      * @throws JSONException If the key is null.
1036      */
put(String key, long value)1037     public JSONObject put(String key, long value) throws JSONException {
1038         put(key, new Long(value));
1039         return this;
1040     }
1041 
1042 
1043     /**
1044      * Put a key/value pair in the JSONObject, where the value will be a
1045      * JSONObject which is produced from a Map.
1046      * @param key   A key string.
1047      * @param value A Map value.
1048      * @return      this.
1049      * @throws JSONException
1050      */
put(String key, Map value)1051     public JSONObject put(String key, Map value) throws JSONException {
1052         put(key, new JSONObject(value));
1053         return this;
1054     }
1055 
1056 
1057     /**
1058      * Put a key/value pair in the JSONObject. If the value is null,
1059      * then the key will be removed from the JSONObject if it is present.
1060      * @param key   A key string.
1061      * @param value An object which is the value. It should be of one of these
1062      *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1063      *  or the JSONObject.NULL object.
1064      * @return this.
1065      * @throws JSONException If the value is non-finite number
1066      *  or if the key is null.
1067      */
put(String key, Object value)1068     public JSONObject put(String key, Object value) throws JSONException {
1069         if (key == null) {
1070             throw new JSONException("Null key.");
1071         }
1072         if (value != null) {
1073             testValidity(value);
1074             this.map.put(key, value);
1075         } else {
1076             remove(key);
1077         }
1078         return this;
1079     }
1080 
1081 
1082     /**
1083      * Put a key/value pair in the JSONObject, but only if the key and the
1084      * value are both non-null, and only if there is not already a member
1085      * with that name.
1086      * @param key
1087      * @param value
1088      * @return his.
1089      * @throws JSONException if the key is a duplicate
1090      */
putOnce(String key, Object value)1091     public JSONObject putOnce(String key, Object value) throws JSONException {
1092         if (key != null && value != null) {
1093             if (opt(key) != null) {
1094                 throw new JSONException("Duplicate key \"" + key + "\"");
1095             }
1096             put(key, value);
1097         }
1098         return this;
1099     }
1100 
1101 
1102     /**
1103      * Put a key/value pair in the JSONObject, but only if the
1104      * key and the value are both non-null.
1105      * @param key   A key string.
1106      * @param value An object which is the value. It should be of one of these
1107      *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1108      *  or the JSONObject.NULL object.
1109      * @return this.
1110      * @throws JSONException If the value is a non-finite number.
1111      */
putOpt(String key, Object value)1112     public JSONObject putOpt(String key, Object value) throws JSONException {
1113         if (key != null && value != null) {
1114             put(key, value);
1115         }
1116         return this;
1117     }
1118 
1119 
1120     /**
1121      * Produce a string in double quotes with backslash sequences in all the
1122      * right places. A backslash will be inserted within </, allowing JSON
1123      * text to be delivered in HTML. In JSON text, a string cannot contain a
1124      * control character or an unescaped quote or backslash.
1125      * @param string A String
1126      * @return  A String correctly formatted for insertion in a JSON text.
1127      */
quote(String string)1128     public static String quote(String string) {
1129         if (string == null || string.length() == 0) {
1130             return "\"\"";
1131         }
1132 
1133         char         b;
1134         char         c = 0;
1135         int          i;
1136         int          len = string.length();
1137         StringBuffer sb = new StringBuffer(len + 4);
1138         String       t;
1139 
1140         sb.append('"');
1141         for (i = 0; i < len; i += 1) {
1142             b = c;
1143             c = string.charAt(i);
1144             switch (c) {
1145             case '\\':
1146             case '"':
1147                 sb.append('\\');
1148                 sb.append(c);
1149                 break;
1150             case '/':
1151                 if (b == '<') {
1152                     sb.append('\\');
1153                 }
1154                 sb.append(c);
1155                 break;
1156             case '\b':
1157                 sb.append("\\b");
1158                 break;
1159             case '\t':
1160                 sb.append("\\t");
1161                 break;
1162             case '\n':
1163                 sb.append("\\n");
1164                 break;
1165             case '\f':
1166                 sb.append("\\f");
1167                 break;
1168             case '\r':
1169                 sb.append("\\r");
1170                 break;
1171             default:
1172                 if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
1173                                (c >= '\u2000' && c < '\u2100')) {
1174                     t = "000" + Integer.toHexString(c);
1175                     sb.append("\\u" + t.substring(t.length() - 4));
1176                 } else {
1177                     sb.append(c);
1178                 }
1179             }
1180         }
1181         sb.append('"');
1182         return sb.toString();
1183     }
1184 
1185     /**
1186      * Remove a name and its value, if present.
1187      * @param key The name to be removed.
1188      * @return The value that was associated with the name,
1189      * or null if there was no value.
1190      */
remove(String key)1191     public Object remove(String key) {
1192         return this.map.remove(key);
1193     }
1194 
1195     /**
1196      * Get an enumeration of the keys of the JSONObject.
1197      * The keys will be sorted alphabetically.
1198      *
1199      * @return An iterator of the keys.
1200      */
sortedKeys()1201     public Iterator sortedKeys() {
1202       return new TreeSet(this.map.keySet()).iterator();
1203     }
1204 
1205     /**
1206      * Try to convert a string into a number, boolean, or null. If the string
1207      * can't be converted, return the string.
1208      * @param s A String.
1209      * @return A simple JSON value.
1210      */
stringToValue(String s)1211     static public Object stringToValue(String s) {
1212         if (s.equals("")) {
1213             return s;
1214         }
1215         if (s.equalsIgnoreCase("true")) {
1216             return Boolean.TRUE;
1217         }
1218         if (s.equalsIgnoreCase("false")) {
1219             return Boolean.FALSE;
1220         }
1221         if (s.equalsIgnoreCase("null")) {
1222             return JSONObject.NULL;
1223         }
1224 
1225         /*
1226          * If it might be a number, try converting it. We support the 0- and 0x-
1227          * conventions. If a number cannot be produced, then the value will just
1228          * be a string. Note that the 0-, 0x-, plus, and implied string
1229          * conventions are non-standard. A JSON parser is free to accept
1230          * non-JSON forms as long as it accepts all correct JSON forms.
1231          */
1232 
1233         char b = s.charAt(0);
1234         if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
1235             if (b == '0') {
1236                 if (s.length() > 2 &&
1237                         (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
1238                     try {
1239                         return new Integer(Integer.parseInt(s.substring(2),
1240                                 16));
1241                     } catch (Exception e) {
1242                         /* Ignore the error */
1243                     }
1244                 } else {
1245                     try {
1246                         return new Integer(Integer.parseInt(s, 8));
1247                     } catch (Exception e) {
1248                         /* Ignore the error */
1249                     }
1250                 }
1251             }
1252             try {
1253                 if (s.indexOf('.') > -1 || s.indexOf('e') > -1 || s.indexOf('E') > -1) {
1254                     return Double.valueOf(s);
1255                 } else {
1256                     Long myLong = new Long(s);
1257                     if (myLong.longValue() == myLong.intValue()) {
1258                         return new Integer(myLong.intValue());
1259                     } else {
1260                         return myLong;
1261                     }
1262                 }
1263             }  catch (Exception f) {
1264                 /* Ignore the error */
1265             }
1266         }
1267         return s;
1268     }
1269 
1270 
1271     /**
1272      * Throw an exception if the object is an NaN or infinite number.
1273      * @param o The object to test.
1274      * @throws JSONException If o is a non-finite number.
1275      */
testValidity(Object o)1276     static void testValidity(Object o) throws JSONException {
1277         if (o != null) {
1278             if (o instanceof Double) {
1279                 if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
1280                     throw new JSONException(
1281                         "JSON does not allow non-finite numbers.");
1282                 }
1283             } else if (o instanceof Float) {
1284                 if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
1285                     throw new JSONException(
1286                         "JSON does not allow non-finite numbers.");
1287                 }
1288             }
1289         }
1290     }
1291 
1292 
1293     /**
1294      * Produce a JSONArray containing the values of the members of this
1295      * JSONObject.
1296      * @param names A JSONArray containing a list of key strings. This
1297      * determines the sequence of the values in the result.
1298      * @return A JSONArray of values.
1299      * @throws JSONException If any of the values are non-finite numbers.
1300      */
toJSONArray(JSONArray names)1301     public JSONArray toJSONArray(JSONArray names) throws JSONException {
1302         if (names == null || names.length() == 0) {
1303             return null;
1304         }
1305         JSONArray ja = new JSONArray();
1306         for (int i = 0; i < names.length(); i += 1) {
1307             ja.put(this.opt(names.getString(i)));
1308         }
1309         return ja;
1310     }
1311 
1312     /**
1313      * Make a JSON text of this JSONObject. For compactness, no whitespace
1314      * is added. If this would not result in a syntactically correct JSON text,
1315      * then null will be returned instead.
1316      * <p>
1317      * Warning: This method assumes that the data structure is acyclical.
1318      *
1319      * @return a printable, displayable, portable, transmittable
1320      *  representation of the object, beginning
1321      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1322      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1323      */
toString()1324     public String toString() {
1325         try {
1326             Iterator     keys = keys();
1327             StringBuffer sb = new StringBuffer("{");
1328 
1329             while (keys.hasNext()) {
1330                 if (sb.length() > 1) {
1331                     sb.append(',');
1332                 }
1333                 Object o = keys.next();
1334                 sb.append(quote(o.toString()));
1335                 sb.append(':');
1336                 sb.append(valueToString(this.map.get(o)));
1337             }
1338             sb.append('}');
1339             return sb.toString();
1340         } catch (Exception e) {
1341             return null;
1342         }
1343     }
1344 
1345 
1346     /**
1347      * Make a prettyprinted JSON text of this JSONObject.
1348      * <p>
1349      * Warning: This method assumes that the data structure is acyclical.
1350      * @param indentFactor The number of spaces to add to each level of
1351      *  indentation.
1352      * @return a printable, displayable, portable, transmittable
1353      *  representation of the object, beginning
1354      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1355      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1356      * @throws JSONException If the object contains an invalid number.
1357      */
toString(int indentFactor)1358     public String toString(int indentFactor) throws JSONException {
1359         return toString(indentFactor, 0);
1360     }
1361 
1362 
1363     /**
1364      * Make a prettyprinted JSON text of this JSONObject.
1365      * <p>
1366      * Warning: This method assumes that the data structure is acyclical.
1367      * @param indentFactor The number of spaces to add to each level of
1368      *  indentation.
1369      * @param indent The indentation of the top level.
1370      * @return a printable, displayable, transmittable
1371      *  representation of the object, beginning
1372      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1373      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1374      * @throws JSONException If the object contains an invalid number.
1375      */
toString(int indentFactor, int indent)1376     String toString(int indentFactor, int indent) throws JSONException {
1377         int j;
1378         int n = length();
1379         if (n == 0) {
1380             return "{}";
1381         }
1382         Iterator     keys = sortedKeys();
1383         StringBuffer sb = new StringBuffer("{");
1384         int          newindent = indent + indentFactor;
1385         Object       o;
1386         if (n == 1) {
1387             o = keys.next();
1388             sb.append(quote(o.toString()));
1389             sb.append(": ");
1390             sb.append(valueToString(this.map.get(o), indentFactor,
1391                     indent));
1392         } else {
1393             while (keys.hasNext()) {
1394                 o = keys.next();
1395                 if (sb.length() > 1) {
1396                     sb.append(",\n");
1397                 } else {
1398                     sb.append('\n');
1399                 }
1400                 for (j = 0; j < newindent; j += 1) {
1401                     sb.append(' ');
1402                 }
1403                 sb.append(quote(o.toString()));
1404                 sb.append(": ");
1405                 sb.append(valueToString(this.map.get(o), indentFactor,
1406                         newindent));
1407             }
1408             if (sb.length() > 1) {
1409                 sb.append('\n');
1410                 for (j = 0; j < indent; j += 1) {
1411                     sb.append(' ');
1412                 }
1413             }
1414         }
1415         sb.append('}');
1416         return sb.toString();
1417     }
1418 
1419 
1420     /**
1421      * Make a JSON text of an Object value. If the object has an
1422      * value.toJSONString() method, then that method will be used to produce
1423      * the JSON text. The method is required to produce a strictly
1424      * conforming text. If the object does not contain a toJSONString
1425      * method (which is the most common case), then a text will be
1426      * produced by other means. If the value is an array or Collection,
1427      * then a JSONArray will be made from it and its toJSONString method
1428      * will be called. If the value is a MAP, then a JSONObject will be made
1429      * from it and its toJSONString method will be called. Otherwise, the
1430      * value's toString method will be called, and the result will be quoted.
1431      *
1432      * <p>
1433      * Warning: This method assumes that the data structure is acyclical.
1434      * @param value The value to be serialized.
1435      * @return a printable, displayable, transmittable
1436      *  representation of the object, beginning
1437      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1438      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1439      * @throws JSONException If the value is or contains an invalid number.
1440      */
valueToString(Object value)1441     static String valueToString(Object value) throws JSONException {
1442         if (value == null || value.equals(null)) {
1443             return "null";
1444         }
1445         if (value instanceof JSONString) {
1446             Object o;
1447             try {
1448                 o = ((JSONString)value).toJSONString();
1449             } catch (Exception e) {
1450                 throw new JSONException(e);
1451             }
1452             if (o instanceof String) {
1453                 return (String)o;
1454             }
1455             throw new JSONException("Bad value from toJSONString: " + o);
1456         }
1457         if (value instanceof Number) {
1458             return numberToString((Number) value);
1459         }
1460         if (value instanceof Boolean || value instanceof JSONObject ||
1461                 value instanceof JSONArray) {
1462             return value.toString();
1463         }
1464         if (value instanceof Map) {
1465             return new JSONObject((Map)value).toString();
1466         }
1467         if (value instanceof Collection) {
1468             return new JSONArray((Collection)value).toString();
1469         }
1470         if (value.getClass().isArray()) {
1471             return new JSONArray(value).toString();
1472         }
1473         return quote(value.toString());
1474     }
1475 
1476 
1477     /**
1478      * Make a prettyprinted JSON text of an object value.
1479      * <p>
1480      * Warning: This method assumes that the data structure is acyclical.
1481      * @param value The value to be serialized.
1482      * @param indentFactor The number of spaces to add to each level of
1483      *  indentation.
1484      * @param indent The indentation of the top level.
1485      * @return a printable, displayable, transmittable
1486      *  representation of the object, beginning
1487      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1488      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1489      * @throws JSONException If the object contains an invalid number.
1490      */
valueToString(Object value, int indentFactor, int indent)1491      static String valueToString(Object value, int indentFactor, int indent)
1492             throws JSONException {
1493         if (value == null || value.equals(null)) {
1494             return "null";
1495         }
1496         try {
1497             if (value instanceof JSONString) {
1498                 Object o = ((JSONString)value).toJSONString();
1499                 if (o instanceof String) {
1500                     return (String)o;
1501                 }
1502             }
1503         } catch (Exception e) {
1504             /* forget about it */
1505         }
1506         if (value instanceof Number) {
1507             return numberToString((Number) value);
1508         }
1509         if (value instanceof Boolean) {
1510             return value.toString();
1511         }
1512         if (value instanceof JSONObject) {
1513             return ((JSONObject)value).toString(indentFactor, indent);
1514         }
1515         if (value instanceof JSONArray) {
1516             return ((JSONArray)value).toString(indentFactor, indent);
1517         }
1518         if (value instanceof Map) {
1519             return new JSONObject((Map)value).toString(indentFactor, indent);
1520         }
1521         if (value instanceof Collection) {
1522             return new JSONArray((Collection)value).toString(indentFactor, indent);
1523         }
1524         if (value.getClass().isArray()) {
1525             return new JSONArray(value).toString(indentFactor, indent);
1526         }
1527         return quote(value.toString());
1528     }
1529 
1530 
1531      /**
1532       * Write the contents of the JSONObject as JSON text to a writer.
1533       * For compactness, no whitespace is added.
1534       * <p>
1535       * Warning: This method assumes that the data structure is acyclical.
1536       *
1537       * @return The writer.
1538       * @throws JSONException
1539       */
write(Writer writer)1540      public Writer write(Writer writer) throws JSONException {
1541         try {
1542             boolean  b = false;
1543             Iterator keys = keys();
1544             writer.write('{');
1545 
1546             while (keys.hasNext()) {
1547                 if (b) {
1548                     writer.write(',');
1549                 }
1550                 Object k = keys.next();
1551                 writer.write(quote(k.toString()));
1552                 writer.write(':');
1553                 Object v = this.map.get(k);
1554                 if (v instanceof JSONObject) {
1555                     ((JSONObject)v).write(writer);
1556                 } else if (v instanceof JSONArray) {
1557                     ((JSONArray)v).write(writer);
1558                 } else {
1559                     writer.write(valueToString(v));
1560                 }
1561                 b = true;
1562             }
1563             writer.write('}');
1564             return writer;
1565         } catch (IOException e) {
1566             throw new JSONException(e);
1567         }
1568      }
1569 }