View Javadoc

1   package atg.taglib.json.util;
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.util.HashMap;
30  import java.util.Iterator;
31  import java.util.LinkedHashMap;
32  import java.util.Map;
33  
34  /**
35   * A JSONObject is an unordered collection of name/value pairs. Its
36   * external form is a string wrapped in curly braces with colons between the
37   * names and values, and commas between the values and names. The internal form
38   * is an object having <code>get</code> and <code>opt</code> methods for
39   * accessing the values by name, and <code>put</code> methods for adding or
40   * replacing values by name. The values can be any of these types:
41   * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
42   * <code>Number</code>, <code>String</code>, or the <code>JSONObject.NULL</code>
43   * object. A JSONObject constructor can be used to convert an external form
44   * JSON text into an internal form whose values can be retrieved with the
45   * <code>get</code> and <code>opt</code> methods, or to convert values into a
46   * JSON text using the <code>put</code> and <code>toString</code> methods.
47   * A <code>get</code> method returns a value if one can be found, and throws an
48   * exception if one cannot be found. An <code>opt</code> method returns a
49   * default value instead of throwing an exception, and so is useful for
50   * obtaining optional values.
51   * <p>
52   * The generic <code>get()</code> and <code>opt()</code> methods return an
53   * object, which you can cast or query for type. There are also typed
54   * <code>get</code> and <code>opt</code> methods that do type checking and type
55   * coersion for you.
56   * <p>
57   * The <code>put</code> methods adds values to an object. For example, <pre>
58   *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
59   * produces the string <code>{"JSON": "Hello, World"}</code>.
60   * <p>
61   * The texts produced by the <code>toString</code> methods strictly conform to
62   * the JSON sysntax rules.
63   * The constructors are more forgiving in the texts they will accept:
64   * <ul>
65   * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
66   *     before the closing brace.</li>
67   * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
68   *     quote)</small>.</li>
69   * <li>Strings do not need to be quoted at all if they do not begin with a quote
70   *     or single quote, and if they do not contain leading or trailing spaces,
71   *     and if they do not contain any of these characters:
72   *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
73   *     and if they are not the reserved words <code>true</code>,
74   *     <code>false</code>, or <code>null</code>.</li>
75   * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
76   *     by <code>:</code>.</li>
77   * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
78   *     well as by <code>,</code> <small>(comma)</small>.</li>
79   * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
80   *     <code>0x-</code> <small>(hex)</small> prefix.</li>
81   * <li>Comments written in the slashshlash, slashstar, and hash conventions
82   *     will be ignored.</li>
83   * </ul>
84   * @author JSON.org
85   * @version 2
86   */
87  public class JSONObject {
88  
89      /**
90       * JSONObject.NULL is equivalent to the value that JavaScript calls null,
91       * whilst Java's null is equivalent to the value that JavaScript calls
92       * undefined.
93       */
94       private static final class Null {
95  
96          /**
97           * There is only intended to be a single instance of the NULL object,
98           * so the clone method returns itself.
99           * @return     NULL.
100          */
101         protected final Object clone() {
102             return this;
103         }
104 
105 
106         /**
107          * A Null object is equal to the null value and to itself.
108          * @param object    An object to test for nullness.
109          * @return true if the object parameter is the JSONObject.NULL object
110          *  or null.
111          */
112         public boolean equals(Object object) {
113             return object == null || object == this;
114         }
115 
116         
117         public int hashCode()
118         {
119           return 1;
120         }
121 
122 
123         /**
124          * Get the "null" string value.
125          * @return The string "null".
126          */
127         public String toString() {
128             return "null";
129         }
130     }
131 
132 
133     /**
134      * The hash map where the JSONObject's properties are kept.
135      */
136     private HashMap myHashMap;
137 
138 
139     /**
140      * It is sometimes more convenient and less ambiguous to have a
141      * <code>NULL</code> object than to use Java's <code>null</code> value.
142      * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
143      * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
144      */
145     public static final Object NULL = new Null();
146 
147 
148     /**
149      * Construct an empty JSONObject.
150      */
151     public JSONObject() {
152         this.myHashMap = new LinkedHashMap();
153     }
154 
155 
156     /**
157      * Construct a JSONObject from a subset of another JSONObject.
158      * An array of strings is used to identify the keys that should be copied.
159      * Missing keys are ignored.
160      * @param jo A JSONObject.
161      * @param sa An array of strings.
162      * @exception JSONException If a value is a non-finite number.
163      */
164     public JSONObject(JSONObject jo, String[] sa) throws JSONException {
165         this();
166         for (int i = 0; i < sa.length; i += 1) {
167             putOpt(sa[i], jo.opt(sa[i]));
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      */
177     public JSONObject(JSONTokener x) throws JSONException {
178         this();
179         char c;
180         String key;
181 
182         if (x.nextClean() != '{') {
183             throw x.syntaxError("A JSONObject text must begin with '{'");
184         }
185         for (;;) {
186             c = x.nextClean();
187             switch (c) {
188             case 0:
189                 throw x.syntaxError("A JSONObject text must end with '}'");
190             case '}':
191                 return;
192             default:
193                 x.back();
194                 key = x.nextValue().toString();
195             }
196 
197             /*
198              * The key is followed by ':'. We will also tolerate '=' or '=>'.
199              */
200 
201             c = x.nextClean();
202             if (c == '=') {
203                 if (x.next() != '>') {
204                     x.back();
205                 }
206             } else if (c != ':') {
207                 throw x.syntaxError("Expected a ':' after a key");
208             }
209             this.myHashMap.put(key, x.nextValue());
210 
211             /*
212              * Pairs are separated by ','. We will also tolerate ';'.
213              */
214 
215             switch (x.nextClean()) {
216             case ';':
217             case ',':
218                 if (x.nextClean() == '}') {
219                     return;
220                 }
221                 x.back();
222                 break;
223             case '}':
224                 return;
225             default:
226                 throw x.syntaxError("Expected a ',' or '}'");
227             }
228         }
229     }
230 
231 
232     /**
233      * Construct a JSONObject from a Map.
234      * @param map A map object that can be used to initialize the contents of
235      *  the JSONObject.
236      */
237     public JSONObject(Map map) {
238         this.myHashMap = new HashMap(map);
239     }
240 
241 
242     /**
243      * Construct a JSONObject from a string.
244      * This is the most commonly used JSONObject constructor.
245      * @param string    A string beginning
246      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
247      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
248      * @exception JSONException If there is a syntax error in the source string.
249      */
250     public JSONObject(String string) throws JSONException {
251         this(new JSONTokener(string));
252     }
253 
254 
255     /**
256      * Accumulate values under a key. It is similar to the put method except
257      * that if there is already an object stored under the key then a
258      * JSONArray is stored under the key to hold all of the accumulated values.
259      * If there is already a JSONArray, then the new value is appended to it.
260      * In contrast, the put method replaces the previous value.
261      * @param key   A key string.
262      * @param value An object to be accumulated under the key.
263      * @return this.
264      * @throws JSONException If the value is an invalid number
265      *  or if the key is null.
266      */
267     public JSONObject accumulate(String key, Object value)
268             throws JSONException {
269         testValidity(value);
270         Object o = opt(key);
271         if (o == null) {
272             put(key, value);
273         } else if (o instanceof JSONArray) {
274             ((JSONArray)o).put(value);
275         } else {
276             put(key, new JSONArray().put(o).put(value));
277         }
278         return this;
279     }
280 
281 
282     /**
283      * Get the value object associated with a key.
284      *
285      * @param key   A key string.
286      * @return      The object associated with the key.
287      * @throws   JSONException if the key is not found.
288      */
289     public Object get(String key) throws JSONException {
290         Object o = opt(key);
291         if (o == null) {
292             throw new JSONException("JSONObject[" + quote(key) +
293                     "] not found.");
294         }
295         return o;
296     }
297 
298 
299     /**
300      * Get the boolean value associated with a key.
301      *
302      * @param key   A key string.
303      * @return      The truth.
304      * @throws   JSONException
305      *  if the value is not a Boolean or the String "true" or "false".
306      */
307     public boolean getBoolean(String key) throws JSONException {
308         Object o = get(key);
309         if (o.equals(Boolean.FALSE) ||
310                 (o instanceof String &&
311                 ((String)o).equalsIgnoreCase("false"))) {
312             return false;
313         } else if (o.equals(Boolean.TRUE) ||
314                 (o instanceof String &&
315                 ((String)o).equalsIgnoreCase("true"))) {
316             return true;
317         }
318         throw new JSONException("JSONObject[" + quote(key) +
319                 "] is not a Boolean.");
320     }
321 
322 
323     /**
324      * Get the double value associated with a key.
325      * @param key   A key string.
326      * @return      The numeric value.
327      * @throws JSONException if the key is not found or
328      *  if the value is not a Number object and cannot be converted to a number.
329      */
330     public double getDouble(String key) throws JSONException {
331         Object o = get(key);
332         try {
333             return o instanceof Number ?
334                     ((Number)o).doubleValue() : Double.parseDouble((String)o);
335         } catch (Exception e) {
336             throw new JSONException("JSONObject[" + quote(key) +
337                 "] is not a number.");
338         }
339     }
340 
341 
342     /**
343      * Get the int value associated with a key. If the number value is too
344      * large for an int, it will be clipped.
345      *
346      * @param key   A key string.
347      * @return      The integer value.
348      * @throws   JSONException if the key is not found or if the value cannot
349      *  be converted to an integer.
350      */
351     public int getInt(String key) throws JSONException {
352         Object o = get(key);
353         return o instanceof Number ?
354                 ((Number)o).intValue() : (int)getDouble(key);
355     }
356 
357 
358     /**
359      * Get the JSONArray value associated with a key.
360      *
361      * @param key   A key string.
362      * @return      A JSONArray which is the value.
363      * @throws   JSONException if the key is not found or
364      *  if the value is not a JSONArray.
365      */
366     public JSONArray getJSONArray(String key) throws JSONException {
367         Object o = get(key);
368         if (o instanceof JSONArray) {
369             return (JSONArray)o;
370         }
371         throw new JSONException("JSONObject[" + quote(key) +
372                 "] is not a JSONArray.");
373     }
374 
375 
376     /**
377      * Get the JSONObject value associated with a key.
378      *
379      * @param key   A key string.
380      * @return      A JSONObject which is the value.
381      * @throws   JSONException if the key is not found or
382      *  if the value is not a JSONObject.
383      */
384     public JSONObject getJSONObject(String key) throws JSONException {
385         Object o = get(key);
386         if (o instanceof JSONObject) {
387             return (JSONObject)o;
388         }
389         throw new JSONException("JSONObject[" + quote(key) +
390                 "] is not a JSONObject.");
391     }
392 
393 
394     /**
395      * Get the long value associated with a key. If the number value is too
396      * long for a long, it will be clipped.
397      *
398      * @param key   A key string.
399      * @return      The long value.
400      * @throws   JSONException if the key is not found or if the value cannot
401      *  be converted to a long.
402      */
403     public long getLong(String key) throws JSONException {
404         Object o = get(key);
405         return o instanceof Number ?
406                 ((Number)o).longValue() : (long)getDouble(key);
407     }
408 
409 
410     /**
411      * Get the string associated with a key.
412      *
413      * @param key   A key string.
414      * @return      A string which is the value.
415      * @throws   JSONException if the key is not found.
416      */
417     public String getString(String key) throws JSONException {
418         return get(key).toString();
419     }
420 
421 
422     /**
423      * Determine if the JSONObject contains a specific key.
424      * @param key   A key string.
425      * @return      true if the key exists in the JSONObject.
426      */
427     public boolean has(String key) {
428         return this.myHashMap.containsKey(key);
429     }
430 
431 
432     /**
433      * Determine if the value associated with the key is null or if there is
434      *  no value.
435      * @param key   A key string.
436      * @return      true if there is no value associated with the key or if
437      *  the value is the JSONObject.NULL object.
438      */
439     public boolean isNull(String key) {
440         return JSONObject.NULL.equals(opt(key));
441     }
442 
443 
444     /**
445      * Get an enumeration of the keys of the JSONObject.
446      *
447      * @return An iterator of the keys.
448      */
449     public Iterator keys() {
450         return this.myHashMap.keySet().iterator();
451     }
452 
453 
454     /**
455      * Get the number of keys stored in the JSONObject.
456      *
457      * @return The number of keys in the JSONObject.
458      */
459     public int length() {
460         return this.myHashMap.size();
461     }
462 
463 
464     /**
465      * Produce a JSONArray containing the names of the elements of this
466      * JSONObject.
467      * @return A JSONArray containing the key strings, or null if the JSONObject
468      * is empty.
469      */
470     public JSONArray names() {
471         JSONArray ja = new JSONArray();
472         Iterator  keys = keys();
473         while (keys.hasNext()) {
474             ja.put(keys.next());
475         }
476         return ja.length() == 0 ? null : ja;
477     }
478 
479     /**
480      * Produce a string from a number.
481      * @param  n A Number
482      * @return A String.
483      * @throws JSONException If n is a non-finite number.
484      */
485     static public String numberToString(Number n)
486             throws JSONException {
487         if (n == null) {
488             throw new JSONException("Null pointer");
489         }
490         testValidity(n);
491 
492 // Shave off trailing zeros and decimal point, if possible.
493 
494         String s = n.toString();
495         if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
496             while (s.endsWith("0")) {
497                 s = s.substring(0, s.length() - 1);
498             }
499             if (s.endsWith(".")) {
500                 s = s.substring(0, s.length() - 1);
501             }
502         }
503         return s;
504     }
505 
506 
507     /**
508      * Get an optional value associated with a key.
509      * @param key   A key string.
510      * @return      An object which is the value, or null if there is no value.
511      */
512     public Object opt(String key) {
513         return key == null ? null : this.myHashMap.get(key);
514     }
515 
516 
517     /**
518      * Get an optional boolean associated with a key.
519      * It returns false if there is no such key, or if the value is not
520      * Boolean.TRUE or the String "true".
521      *
522      * @param key   A key string.
523      * @return      The truth.
524      */
525     public boolean optBoolean(String key) {
526         return optBoolean(key, false);
527     }
528 
529 
530     /**
531      * Get an optional boolean associated with a key.
532      * It returns the defaultValue if there is no such key, or if it is not
533      * a Boolean or the String "true" or "false" (case insensitive).
534      *
535      * @param key              A key string.
536      * @param defaultValue     The default.
537      * @return      The truth.
538      */
539     public boolean optBoolean(String key, boolean defaultValue) {
540         try {
541             return getBoolean(key);
542         } catch (Exception e) {
543             return defaultValue;
544         }
545     }
546 
547 
548     /**
549      * Get an optional double associated with a key,
550      * or NaN if there is no such key or if its value is not a number.
551      * If the value is a string, an attempt will be made to evaluate it as
552      * a number.
553      *
554      * @param key   A string which is the key.
555      * @return      An object which is the value.
556      */
557     public double optDouble(String key) {
558         return optDouble(key, Double.NaN);
559     }
560 
561 
562     /**
563      * Get an optional double associated with a key, or the
564      * defaultValue if there is no such key or if its value is not a number.
565      * If the value is a string, an attempt will be made to evaluate it as
566      * a number.
567      *
568      * @param key   A key string.
569      * @param defaultValue     The default.
570      * @return      An object which is the value.
571      */
572     public double optDouble(String key, double defaultValue) {
573         try {
574             Object o = opt(key);
575             return o instanceof Number ? ((Number)o).doubleValue() :
576                 new Double((String)o).doubleValue();
577         } catch (Exception e) {
578             return defaultValue;
579         }
580     }
581 
582 
583     /**
584      * Get an optional int value associated with a key,
585      * or zero if there is no such key or if the value is not a number.
586      * If the value is a string, an attempt will be made to evaluate it as
587      * a number.
588      *
589      * @param key   A key string.
590      * @return      An object which is the value.
591      */
592     public int optInt(String key) {
593         return optInt(key, 0);
594     }
595 
596 
597     /**
598      * Get an optional int value associated with a key,
599      * or the default if there is no such key or if the value is not a number.
600      * If the value is a string, an attempt will be made to evaluate it as
601      * a number.
602      *
603      * @param key   A key string.
604      * @param defaultValue     The default.
605      * @return      An object which is the value.
606      */
607     public int optInt(String key, int defaultValue) {
608         try {
609             return getInt(key);
610         } catch (Exception e) {
611             return defaultValue;
612         }
613     }
614 
615 
616     /**
617      * Get an optional JSONArray associated with a key.
618      * It returns null if there is no such key, or if its value is not a
619      * JSONArray.
620      *
621      * @param key   A key string.
622      * @return      A JSONArray which is the value.
623      */
624     public JSONArray optJSONArray(String key) {
625         Object o = opt(key);
626         return o instanceof JSONArray ? (JSONArray)o : null;
627     }
628 
629 
630     /**
631      * Get an optional JSONObject associated with a key.
632      * It returns null if there is no such key, or if its value is not a
633      * JSONObject.
634      *
635      * @param key   A key string.
636      * @return      A JSONObject which is the value.
637      */
638     public JSONObject optJSONObject(String key) {
639         Object o = opt(key);
640         return o instanceof JSONObject ? (JSONObject)o : null;
641     }
642 
643 
644     /**
645      * Get an optional long value associated with a key,
646      * or zero if there is no such key or if the value is not a number.
647      * If the value is a string, an attempt will be made to evaluate it as
648      * a number.
649      *
650      * @param key   A key string.
651      * @return      An object which is the value.
652      */
653     public long optLong(String key) {
654         return optLong(key, 0);
655     }
656 
657 
658     /**
659      * Get an optional long value associated with a key,
660      * or the default if there is no such key or if the value is not a number.
661      * If the value is a string, an attempt will be made to evaluate it as
662      * a number.
663      *
664      * @param key   A key string.
665      * @param defaultValue     The default.
666      * @return      An object which is the value.
667      */
668     public long optLong(String key, long defaultValue) {
669         try {
670             return getLong(key);
671         } catch (Exception e) {
672             return defaultValue;
673         }
674     }
675 
676 
677     /**
678      * Get an optional string associated with a key.
679      * It returns an empty string if there is no such key. If the value is not
680      * a string and is not null, then it is coverted to a string.
681      *
682      * @param key   A key string.
683      * @return      A string which is the value.
684      */
685     public String optString(String key) {
686         return optString(key, "");
687     }
688 
689 
690     /**
691      * Get an optional string associated with a key.
692      * It returns the defaultValue if there is no such key.
693      *
694      * @param key   A key string.
695      * @param defaultValue     The default.
696      * @return      A string which is the value.
697      */
698     public String optString(String key, String defaultValue) {
699         Object o = opt(key);
700         return o != null ? o.toString() : defaultValue;
701     }
702 
703 
704     /**
705      * Put a key/boolean pair in the JSONObject.
706      *
707      * @param key   A key string.
708      * @param value A boolean which is the value.
709      * @return this.
710      * @throws JSONException If the key is null.
711      */
712     public JSONObject put(String key, boolean value) throws JSONException {
713         put(key, value ? Boolean.TRUE : Boolean.FALSE);
714         return this;
715     }
716 
717 
718     /**
719      * Put a key/double pair in the JSONObject.
720      *
721      * @param key   A key string.
722      * @param value A double which is the value.
723      * @return this.
724      * @throws JSONException If the key is null or if the number is invalid.
725      */
726     public JSONObject put(String key, double value) throws JSONException {
727         put(key, new Double(value));
728         return this;
729     }
730 
731 
732     /**
733      * Put a key/int pair in the JSONObject.
734      *
735      * @param key   A key string.
736      * @param value An int which is the value.
737      * @return this.
738      * @throws JSONException If the key is null.
739      */
740     public JSONObject put(String key, int value) throws JSONException {
741         put(key, new Integer(value));
742         return this;
743     }
744 
745 
746     /**
747      * Put a key/long pair in the JSONObject.
748      *
749      * @param key   A key string.
750      * @param value A long which is the value.
751      * @return this.
752      * @throws JSONException If the key is null.
753      */
754     public JSONObject put(String key, long value) throws JSONException {
755         put(key, new Long(value));
756         return this;
757     }
758 
759 
760     /**
761      * Put a key/value pair in the JSONObject. If the value is null,
762      * then the key will be removed from the JSONObject if it is present.
763      * @param key   A key string.
764      * @param value An object which is the value. It should be of one of these
765      *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
766      *  or the JSONObject.NULL object.
767      * @return this.
768      * @throws JSONException If the value is non-finite number
769      *  or if the key is null.
770      */
771     public JSONObject put(String key, Object value) throws JSONException {
772         if (key == null) {
773             throw new JSONException("Null key.");
774         }
775         if (value != null) {
776             testValidity(value);
777             this.myHashMap.put(key, value);
778         } else {
779             remove(key);
780         }
781         return this;
782     }
783 
784 
785     /**
786      * Put a key/value pair in the JSONObject, but only if the
787      * key and the value are both non-null.
788      * @param key   A key string.
789      * @param value An object which is the value. It should be of one of these
790      *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
791      *  or the JSONObject.NULL object.
792      * @return this.
793      * @throws JSONException If the value is a non-finite number.
794      */
795     public JSONObject putOpt(String key, Object value) throws JSONException {
796         if (key != null && value != null) {
797             put(key, value);
798         }
799         return this;
800     }
801 
802 
803     /**
804      * Produce a string in double quotes with backslash sequences in all the
805      * right places. A backslash will be inserted within </, allowing JSON
806      * text to be delivered in HTML. In JSON text, a string cannot contain a
807      * control character or an unescaped quote or backslash.
808      * @param string A String
809      * @return  A String correctly formatted for insertion in a JSON text.
810      */
811     public static String quote(String string) {
812         if (string == null || string.length() == 0) {
813             return "\"\"";
814         }
815 
816         char         b;
817         char         c = 0;
818         int          i;
819         int          len = string.length();
820         StringBuffer sb = new StringBuffer(len + 4);
821         String       t;
822 
823         sb.append('"');
824         for (i = 0; i < len; i += 1) {
825             b = c;
826             c = string.charAt(i);
827             switch (c) {
828             case '\\':
829             case '"':
830                 sb.append('\\');
831                 sb.append(c);
832                 break;
833             case '/':
834                 if (b == '<') {
835                     sb.append('\\');
836                 }
837                 sb.append(c);
838                 break;
839             case '\b':
840                 sb.append("\\b");
841                 break;
842             case '\t':
843                 sb.append("\\t");
844                 break;
845             case '\n':
846                 sb.append("\\n");
847                 break;
848             case '\f':
849                 sb.append("\\f");
850                 break;
851             case '\r':
852                 sb.append("\\r");
853                 break;
854             default:
855                 if (c < ' ') {
856                     t = "000" + Integer.toHexString(c);
857                     sb.append("\\u" + t.substring(t.length() - 4));
858                 } else {
859                     sb.append(c);
860                 }
861             }
862         }
863         sb.append('"');
864         return sb.toString();
865     }
866 
867     /**
868      * Remove a name and its value, if present.
869      * @param key The name to be removed.
870      * @return The value that was associated with the name,
871      * or null if there was no value.
872      */
873     public Object remove(String key) {
874         return this.myHashMap.remove(key);
875     }
876 
877     /**
878      * Throw an exception if the object is an NaN or infinite number.
879      * @param o The object to test.
880      * @throws JSONException If o is a non-finite number.
881      */
882     static void testValidity(Object o) throws JSONException {
883         if (o != null) {
884             if (o instanceof Double) {
885                 if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
886                     throw new JSONException(
887                         "JSON does not allow non-finite numbers");
888                 }
889             } else if (o instanceof Float) {
890                 if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
891                     throw new JSONException(
892                         "JSON does not allow non-finite numbers.");
893                 }
894             }
895         }
896     }
897 
898 
899     /**
900      * Produce a JSONArray containing the values of the members of this
901      * JSONObject.
902      * @param names A JSONArray containing a list of key strings. This
903      * determines the sequence of the values in the result.
904      * @return A JSONArray of values.
905      * @throws JSONException If any of the values are non-finite numbers.
906      */
907     public JSONArray toJSONArray(JSONArray names) throws JSONException {
908         if (names == null || names.length() == 0) {
909             return null;
910         }
911         JSONArray ja = new JSONArray();
912         for (int i = 0; i < names.length(); i += 1) {
913             ja.put(this.opt(names.getString(i)));
914         }
915         return ja;
916     }
917 
918     /**
919      * Make an JSON text of this JSONObject. For compactness, no whitespace
920      * is added. If this would not result in a syntactically correct JSON text,
921      * then null will be returned instead.
922      * <p>
923      * Warning: This method assumes that the data structure is acyclical.
924      *
925      * @return a printable, displayable, portable, transmittable
926      *  representation of the object, beginning
927      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
928      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
929      */
930     public String toString() {
931         try {
932             Iterator     keys = keys();
933             StringBuffer sb = new StringBuffer("{");
934 
935             while (keys.hasNext()) {
936                 if (sb.length() > 1) {
937                     sb.append(',');
938                 }
939                 Object o = keys.next();
940                 sb.append(quote(o.toString()));
941                 sb.append(':');
942                 sb.append(valueToString(this.myHashMap.get(o)));
943             }
944             sb.append('}');
945             return sb.toString();
946         } catch (Exception e) {
947             return null;
948         }
949     }
950 
951 
952     /**
953      * Make a prettyprinted JSON text of this JSONObject.
954      * <p>
955      * Warning: This method assumes that the data structure is acyclical.
956      * @param indentFactor The number of spaces to add to each level of
957      *  indentation.
958      * @return a printable, displayable, portable, transmittable
959      *  representation of the object, beginning
960      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
961      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
962      * @throws JSONException If the object contains an invalid number.
963      */
964     public String toString(int indentFactor) throws JSONException {
965         return toString(indentFactor, 0);
966     }
967 
968 
969     /**
970      * Make a prettyprinted JSON text of this JSONObject.
971      * <p>
972      * Warning: This method assumes that the data structure is acyclical.
973      * @param indentFactor The number of spaces to add to each level of
974      *  indentation.
975      * @param indent The indentation of the top level.
976      * @return a printable, displayable, transmittable
977      *  representation of the object, beginning
978      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
979      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
980      * @throws JSONException If the object contains an invalid number.
981      */
982     String toString(int indentFactor, int indent) throws JSONException {
983         int          i;
984         int          n = length();
985         if (n == 0) {
986             return "{}";
987         }
988         Iterator     keys = keys();
989         StringBuffer sb = new StringBuffer("{");
990         int          newindent = indent + indentFactor;
991         Object       o;
992         if (n == 1) {
993             o = keys.next();
994             sb.append(quote(o.toString()));
995             sb.append(": ");
996             sb.append(valueToString(this.myHashMap.get(o), indentFactor,
997                     indent));
998         } else {
999             while (keys.hasNext()) {
1000                 o = keys.next();
1001                 if (sb.length() > 1) {
1002                     sb.append(",\n");
1003                 } else {
1004                     sb.append('\n');
1005                 }
1006                 for (i = 0; i < newindent; i += 1) {
1007                     sb.append(' ');
1008                 }
1009                 sb.append(quote(o.toString()));
1010                 sb.append(": ");
1011                 sb.append(valueToString(this.myHashMap.get(o), indentFactor,
1012                         newindent));
1013             }
1014             if (sb.length() > 1) {
1015                 sb.append('\n');
1016                 for (i = 0; i < indent; i += 1) {
1017                     sb.append(' ');
1018                 }
1019             }
1020         }
1021         sb.append('}');
1022         return sb.toString();
1023     }
1024 
1025 
1026     /**
1027      * Make a JSON text of an object value.
1028      * <p>
1029      * Warning: This method assumes that the data structure is acyclical.
1030      * @param value The value to be serialized.
1031      * @return a printable, displayable, transmittable
1032      *  representation of the object, beginning
1033      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1034      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1035      * @throws JSONException If the value is or contains an invalid number.
1036      */
1037     static String valueToString(Object value) throws JSONException {
1038         if (value == null || value.equals(null)) { // the .equals is OK, needed for Null class
1039             return "null";
1040         }
1041         if (value instanceof Number) {
1042             return numberToString((Number) value);
1043         }
1044         if (value instanceof Boolean || value instanceof JSONObject ||
1045                 value instanceof JSONArray) {
1046             return value.toString();
1047         }
1048         return quote(value.toString());
1049     }
1050 
1051 
1052     /**
1053      * Make a prettyprinted JSON text of an object value.
1054      * <p>
1055      * Warning: This method assumes that the data structure is acyclical.
1056      * @param value The value to be serialized.
1057      * @param indentFactor The number of spaces to add to each level of
1058      *  indentation.
1059      * @param indent The indentation of the top level.
1060      * @return a printable, displayable, transmittable
1061      *  representation of the object, beginning
1062      *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1063      *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1064      * @throws JSONException If the object contains an invalid number.
1065      */
1066      static String valueToString(Object value, int indentFactor, int indent)
1067             throws JSONException {
1068         if (value == null || value.equals(null)) { // the .equals is OK, needed for Null class
1069             return "null";
1070         }
1071         if (value instanceof Number) {
1072             return numberToString((Number) value);
1073         }
1074         if (value instanceof Boolean) {
1075             return value.toString();
1076         }
1077         if (value instanceof JSONObject) {
1078             return ((JSONObject)value).toString(indentFactor, indent);
1079         }
1080         if (value instanceof JSONArray) {
1081             return ((JSONArray)value).toString(indentFactor, indent);
1082         }
1083         return quote(value.toString());
1084     }
1085 
1086 
1087      /**
1088       * Write the contents of the JSONObject as JSON text to a writer.
1089       * For compactness, no whitespace is added.
1090       * <p>
1091       * Warning: This method assumes that the data structure is acyclical.
1092       *
1093       * @return The writer.
1094       * @throws JSONException
1095       */
1096      public Writer write(Writer writer) throws JSONException {
1097         try {
1098             boolean  b = false;
1099             Iterator keys = keys();
1100             writer.write('{');
1101 
1102             while (keys.hasNext()) {
1103                 if (b) {
1104                     writer.write(',');
1105                 }
1106                 Object k = keys.next();
1107                 writer.write(quote(k.toString()));
1108                 writer.write(':');
1109                 Object v = this.myHashMap.get(k);
1110                 if (v instanceof JSONObject) {
1111                     ((JSONObject)v).write(writer);
1112                 } else if (v instanceof JSONArray) {
1113                     ((JSONArray)v).write(writer);
1114                 } else {
1115                     writer.write(valueToString(v));
1116                 }
1117                 b = true;
1118             }
1119             writer.write('}');
1120             return writer;
1121         } catch (IOException e) {
1122             throw new JSONException(e);
1123         }
1124      }
1125      
1126    /**
1127    * Test equality of 2 JSON objects. The objects are deemed to be equal if
1128    * they contain the same properties
1129    * @see java.lang.Object#equals(java.lang.Object)
1130    *
1131    * @param object The JSONObject with which to compare to
1132    * @return <code>true</code> if this JSONObject is the same as the object
1133      *          argument; <code>false</code> otherwise.
1134    */
1135   public boolean equals(Object object) {
1136     if (object==null){
1137       return false;
1138     }
1139     JSONObject o = (JSONObject)object;
1140     return (this.toHashMap().equals(o.toHashMap()));
1141    }
1142   
1143   /**
1144    * Return a hashCode for the JSONObject. This returns the hashcode for the underlying
1145    * <code>HashMap</code> that stores the data
1146    * @see java.lang.Object#hashCode()
1147    *
1148    * @return a hash code value for this object.
1149    */
1150   public int hashCode(){
1151     return this.toHashMap().hashCode();
1152   }
1153   
1154   /**
1155    * Get the underlying hash map used to store this JSON Object data
1156    *
1157    * @return The underlying <code>HashMap</code> object.
1158    */
1159   public HashMap toHashMap(){
1160     return this.myHashMap;
1161   }
1162 }