source: josm/trunk/src/org/json/JSONObject.java@ 6625

Last change on this file since 6625 was 6615, checked in by Don-vip, 11 years ago

fix compilation warnings + minor code refactorization

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