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

Last change on this file since 6491 was 6484, checked in by Don-vip, 12 years ago

see #7307, fix #8474 - use org.json as lightweight internal json library (already being used by several plugins)

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