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