1 package atg.taglib.json.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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> <small>(comma)</small> may appear just
66 * before the closing brace.</li>
67 * <li>Strings may be quoted with <code>'</code> <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
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
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> <small>(left brace)</small> and ending
247 * with <code>}</code> <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
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> <small>(left brace)</small> and ending
928 * with <code>}</code> <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> <small>(left brace)</small> and ending
961 * with <code>}</code> <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> <small>(left brace)</small> and ending
979 * with <code>}</code> <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> <small>(left brace)</small> and ending
1034 * with <code>}</code> <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)) {
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> <small>(left brace)</small> and ending
1063 * with <code>}</code> <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)) {
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 }