Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
JSONObject | 102 | 622 | 368 | ||
JSONObject.Null | 109 | 4 | 4 |
1 | package org.json; | |
2 | ||
3 | import java.io.Closeable; | |
4 | ||
5 | /* | |
6 | * | |
7 | * Note: This file has been adapted for SwingJS by Bob Hanson hansonr@stolaf.edu | |
8 | * | |
9 | Copyright (c) 2002 JSON.org | |
10 | ||
11 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
12 | of this software and associated documentation files (the "Software"), to deal | |
13 | in the Software without restriction, including without limitation the rights | |
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
15 | copies of the Software, and to permit persons to whom the Software is | |
16 | furnished to do so, subject to the following conditions: | |
17 | ||
18 | The above copyright notice and this permission notice shall be included in all | |
19 | copies or substantial portions of the Software. | |
20 | ||
21 | The Software shall be used for Good, not Evil. | |
22 | ||
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
29 | SOFTWARE. | |
30 | */ | |
31 | ||
32 | import java.io.IOException; | |
33 | import java.io.StringWriter; | |
34 | import java.io.Writer; | |
35 | import java.lang.annotation.Annotation; | |
36 | import java.lang.reflect.Field; | |
37 | import java.lang.reflect.InvocationTargetException; | |
38 | import java.lang.reflect.Method; | |
39 | import java.lang.reflect.Modifier; | |
40 | import java.math.BigDecimal; | |
41 | import java.math.BigInteger; | |
42 | import java.util.Collection; | |
43 | import java.util.Enumeration; | |
44 | import java.util.HashMap; | |
45 | import java.util.Iterator; | |
46 | import java.util.Locale; | |
47 | import java.util.Map; | |
48 | import java.util.Map.Entry; | |
49 | import java.util.ResourceBundle; | |
50 | import java.util.Set; | |
51 | ||
52 | /** | |
53 | * A JSONObject is an unordered collection of name/value pairs. Its external | |
54 | * form is a string wrapped in curly braces with colons between the names and | |
55 | * values, and commas between the values and names. The internal form is an | |
56 | * object having <code>get</code> and <code>opt</code> methods for accessing the | |
57 | * values by name, and <code>put</code> methods for adding or replacing values | |
58 | * by name. The values can be any of these types: <code>Boolean</code>, | |
59 | * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>, | |
60 | * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject | |
61 | * constructor can be used to convert an external form JSON text into an | |
62 | * internal form whose values can be retrieved with the <code>get</code> and | |
63 | * <code>opt</code> methods, or to convert values into a JSON text using the | |
64 | * <code>put</code> and <code>toString</code> methods. A <code>get</code> method | |
65 | * returns a value if one can be found, and throws an exception if one cannot be | |
66 | * found. An <code>opt</code> method returns a default value instead of throwing | |
67 | * an exception, and so is useful for obtaining optional values. | |
68 | * <p> | |
69 | * The generic <code>get()</code> and <code>opt()</code> methods return an | |
70 | * object, which you can cast or query for type. There are also typed | |
71 | * <code>get</code> and <code>opt</code> methods that do type checking and type | |
72 | * coercion for you. The opt methods differ from the get methods in that they do | |
73 | * not throw. Instead, they return a specified value, such as null. | |
74 | * <p> | |
75 | * The <code>put</code> methods add or replace values in an object. For example, | |
76 | * | |
77 | * <pre> | |
78 | * myString = new JSONObject().put("JSON", "Hello, World!").toString(); | |
79 | * </pre> | |
80 | * | |
81 | * produces the string <code>{"JSON": "Hello, World"}</code>. | |
82 | * <p> | |
83 | * The texts produced by the <code>toString</code> methods strictly conform to | |
84 | * the JSON syntax rules. The constructors are more forgiving in the texts they | |
85 | * will accept: | |
86 | * <ul> | |
87 | * <li>An extra <code>,</code> <small>(comma)</small> may appear just | |
88 | * before the closing brace.</li> | |
89 | * <li>Strings may be quoted with <code>'</code> <small>(single | |
90 | * quote)</small>.</li> | |
91 | * <li>Strings do not need to be quoted at all if they do not begin with a quote | |
92 | * or single quote, and if they do not contain leading or trailing spaces, and | |
93 | * if they do not contain any of these characters: | |
94 | * <code>{ } [ ] / \ : , #</code> and if they do not look like numbers and if | |
95 | * they are not the reserved words <code>true</code>, <code>false</code>, or | |
96 | * <code>null</code>.</li> | |
97 | * </ul> | |
98 | * | |
99 | * @author JSON.org | |
100 | * @version 2016-08-15 | |
101 | */ | |
102 | public class JSONObject | |
103 | { | |
104 | /** | |
105 | * JSONObject.NULL is equivalent to the value that JavaScript calls null, | |
106 | * whilst Java's null is equivalent to the value that JavaScript calls | |
107 | * undefined. | |
108 | */ | |
109 | private static final class Null | |
110 | { | |
111 | ||
112 | /** | |
113 | * There is only intended to be a single instance of the NULL object, so the | |
114 | * clone method returns itself. | |
115 | * | |
116 | * @return NULL. | |
117 | */ | |
118 | 0 | @Override |
119 | protected final Object clone() | |
120 | { | |
121 | 0 | return this; |
122 | } | |
123 | ||
124 | /** | |
125 | * A Null object is equal to the null value and to itself. | |
126 | * | |
127 | * @param object | |
128 | * An object to test for nullness. | |
129 | * @return true if the object parameter is the JSONObject.NULL object or | |
130 | * null. | |
131 | */ | |
132 | 1580 | @Override |
133 | public boolean equals(Object object) | |
134 | { | |
135 | 1580 | return object == null || object == this; |
136 | } | |
137 | ||
138 | /** | |
139 | * A Null object is equal to the null value and to itself. | |
140 | * | |
141 | * @return always returns 0. | |
142 | */ | |
143 | 0 | @Override |
144 | public int hashCode() | |
145 | { | |
146 | 0 | return 0; |
147 | } | |
148 | ||
149 | /** | |
150 | * Get the "null" string value. | |
151 | * | |
152 | * @return The string "null". | |
153 | */ | |
154 | 0 | @Override |
155 | public String toString() | |
156 | { | |
157 | 0 | return "null"; |
158 | } | |
159 | } | |
160 | ||
161 | /** | |
162 | * The map where the JSONObject's properties are kept. | |
163 | */ | |
164 | private final Map<String, Object> map; | |
165 | ||
166 | /** | |
167 | * It is sometimes more convenient and less ambiguous to have a | |
168 | * <code>NULL</code> object than to use Java's <code>null</code> value. | |
169 | * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>. | |
170 | * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>. | |
171 | */ | |
172 | public static final Object NULL = new Null(); | |
173 | ||
174 | /** | |
175 | * Construct an empty JSONObject. | |
176 | */ | |
177 | 283 | public JSONObject() |
178 | { | |
179 | // HashMap is used on purpose to ensure that elements are unordered by | |
180 | // the specification. | |
181 | // JSON tends to be a portable transfer format to allows the container | |
182 | // implementations to rearrange their items for a faster element | |
183 | // retrieval based on associative access. | |
184 | // Therefore, an implementation mustn't rely on the order of the item. | |
185 | 283 | this.map = new HashMap<String, Object>(); |
186 | } | |
187 | ||
188 | /** | |
189 | * Construct a JSONObject from a subset of another JSONObject. An array of | |
190 | * strings is used to identify the keys that should be copied. Missing keys | |
191 | * are ignored. | |
192 | * | |
193 | * @param jo | |
194 | * A JSONObject. | |
195 | * @param names | |
196 | * An array of strings. | |
197 | */ | |
198 | 0 | public JSONObject(JSONObject jo, String[] names) |
199 | { | |
200 | 0 | this(names.length); |
201 | 0 | for (int i = 0; i < names.length; i += 1) |
202 | { | |
203 | 0 | try |
204 | { | |
205 | 0 | this.putOnce(names[i], jo.opt(names[i])); |
206 | } catch (Exception ignore) | |
207 | { | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
212 | /** | |
213 | * Construct a JSONObject from a JSONTokener. | |
214 | * | |
215 | * @param x | |
216 | * A JSONTokener object containing the source string. | |
217 | * @throws JSONException | |
218 | * If there is a syntax error in the source string or a duplicated | |
219 | * key. | |
220 | */ | |
221 | 0 | public JSONObject(JSONTokener x) throws JSONException |
222 | { | |
223 | 0 | this(); |
224 | 0 | char c; |
225 | 0 | String key; |
226 | ||
227 | 0 | if (x.nextClean() != '{') |
228 | { | |
229 | 0 | throw x.syntaxError("A JSONObject text must begin with '{'"); |
230 | } | |
231 | 0 | for (;;) |
232 | { | |
233 | 0 | c = x.nextClean(); |
234 | 0 | switch (c) |
235 | { | |
236 | 0 | case 0: |
237 | 0 | throw x.syntaxError("A JSONObject text must end with '}'"); |
238 | 0 | case '}': |
239 | 0 | return; |
240 | 0 | default: |
241 | 0 | x.back(); |
242 | 0 | key = x.nextValue().toString(); |
243 | } | |
244 | ||
245 | // The key is followed by ':'. | |
246 | ||
247 | 0 | c = x.nextClean(); |
248 | 0 | if (c != ':') |
249 | { | |
250 | 0 | throw x.syntaxError("Expected a ':' after a key"); |
251 | } | |
252 | ||
253 | // Use syntaxError(..) to include error location | |
254 | ||
255 | 0 | if (key != null) |
256 | { | |
257 | // Check if key exists | |
258 | 0 | if (this.opt(key) != null) |
259 | { | |
260 | // key already exists | |
261 | 0 | throw x.syntaxError("Duplicate key \"" + key + "\""); |
262 | } | |
263 | // Only add value if non-null | |
264 | 0 | Object value = x.nextValue(); |
265 | 0 | if (value != null) |
266 | { | |
267 | 0 | this.put(key, value); |
268 | } | |
269 | } | |
270 | ||
271 | // Pairs are separated by ','. | |
272 | ||
273 | 0 | switch (x.nextClean()) |
274 | { | |
275 | 0 | case ';': |
276 | 0 | case ',': |
277 | 0 | if (x.nextClean() == '}') |
278 | { | |
279 | 0 | return; |
280 | } | |
281 | 0 | x.back(); |
282 | 0 | break; |
283 | 0 | case '}': |
284 | 0 | return; |
285 | 0 | default: |
286 | 0 | throw x.syntaxError("Expected a ',' or '}'"); |
287 | } | |
288 | } | |
289 | } | |
290 | ||
291 | /** | |
292 | * Construct a JSONObject from a Map. | |
293 | * | |
294 | * @param m | |
295 | * A map object that can be used to initialize the contents of the | |
296 | * JSONObject. | |
297 | * @throws JSONException | |
298 | * If a value in the map is non-finite number. | |
299 | * @throws NullPointerException | |
300 | * If a key in the map is <code>null</code> | |
301 | */ | |
302 | 8 | public JSONObject(Map<?, ?> m) |
303 | { | |
304 | 8 | if (m == null) |
305 | { | |
306 | 0 | this.map = new HashMap<String, Object>(); |
307 | } | |
308 | else | |
309 | { | |
310 | 8 | this.map = new HashMap<String, Object>(m.size()); |
311 | 8 | for (final Entry<?, ?> e : m.entrySet()) |
312 | { | |
313 | 50 | if (e.getKey() == null) |
314 | { | |
315 | 0 | throw new NullPointerException("Null key."); |
316 | } | |
317 | 50 | final Object value = e.getValue(); |
318 | 50 | if (value != null) |
319 | { | |
320 | 42 | this.map.put(String.valueOf(e.getKey()), wrap(value)); |
321 | } | |
322 | } | |
323 | } | |
324 | } | |
325 | ||
326 | /** | |
327 | * Construct a JSONObject from an Object using bean getters. It reflects on | |
328 | * all of the public methods of the object. For each of the methods with no | |
329 | * parameters and a name starting with <code>"get"</code> or <code>"is"</code> | |
330 | * followed by an uppercase letter, the method is invoked, and a key and the | |
331 | * value returned from the getter method are put into the new JSONObject. | |
332 | * <p> | |
333 | * The key is formed by removing the <code>"get"</code> or <code>"is"</code> | |
334 | * prefix. If the second remaining character is not upper case, then the first | |
335 | * character is converted to lower case. | |
336 | * <p> | |
337 | * Methods that are <code>static</code>, return <code>void</code>, have | |
338 | * parameters, or are "bridge" methods, are ignored. | |
339 | * <p> | |
340 | * For example, if an object has a method named <code>"getName"</code>, and if | |
341 | * the result of calling <code>object.getName()</code> is | |
342 | * <code>"Larry Fine"</code>, then the JSONObject will contain | |
343 | * <code>"name": "Larry Fine"</code>. | |
344 | * <p> | |
345 | * The {@link JSONPropertyName} annotation can be used on a bean getter to | |
346 | * override key name used in the JSONObject. For example, using the object | |
347 | * above with the <code>getName</code> method, if we annotated it with: | |
348 | * | |
349 | * <pre> | |
350 | * @JSONPropertyName("FullName") | |
351 | * public String getName() | |
352 | * { | |
353 | * return this.name; | |
354 | * } | |
355 | * </pre> | |
356 | * | |
357 | * The resulting JSON object would contain | |
358 | * <code>"FullName": "Larry Fine"</code> | |
359 | * <p> | |
360 | * Similarly, the {@link JSONPropertyName} annotation can be used on non- | |
361 | * <code>get</code> and <code>is</code> methods. We can also override key name | |
362 | * used in the JSONObject as seen below even though the field would normally | |
363 | * be ignored: | |
364 | * | |
365 | * <pre> | |
366 | * @JSONPropertyName("FullName") | |
367 | * public String fullName() | |
368 | * { | |
369 | * return this.name; | |
370 | * } | |
371 | * </pre> | |
372 | * | |
373 | * The resulting JSON object would contain | |
374 | * <code>"FullName": "Larry Fine"</code> | |
375 | * <p> | |
376 | * The {@link JSONPropertyIgnore} annotation can be used to force the bean | |
377 | * property to not be serialized into JSON. If both {@link JSONPropertyIgnore} | |
378 | * and {@link JSONPropertyName} are defined on the same method, a depth | |
379 | * comparison is performed and the one closest to the concrete class being | |
380 | * serialized is used. If both annotations are at the same level, then the | |
381 | * {@link JSONPropertyIgnore} annotation takes precedent and the field is not | |
382 | * serialized. For example, the following declaration would prevent the | |
383 | * <code>getName</code> method from being serialized: | |
384 | * | |
385 | * <pre> | |
386 | * @JSONPropertyName("FullName") | |
387 | * @JSONPropertyIgnore | |
388 | * public String getName() | |
389 | * { | |
390 | * return this.name; | |
391 | * } | |
392 | * </pre> | |
393 | * <p> | |
394 | * | |
395 | * @param bean | |
396 | * An object that has getter methods that should be used to make a | |
397 | * JSONObject. | |
398 | */ | |
399 | 283 | public JSONObject(Object bean) |
400 | { | |
401 | 283 | this(); |
402 | 283 | this.populateMap(bean); |
403 | } | |
404 | ||
405 | /** | |
406 | * Construct a JSONObject from an Object, using reflection to find the public | |
407 | * members. The resulting JSONObject's keys will be the strings from the names | |
408 | * array, and the values will be the field values associated with those keys | |
409 | * in the object. If a key is not found or not visible, then it will not be | |
410 | * copied into the new JSONObject. | |
411 | * | |
412 | * @param object | |
413 | * An object that has fields that should be used to make a | |
414 | * JSONObject. | |
415 | * @param names | |
416 | * An array of strings, the names of the fields to be obtained from | |
417 | * the object. | |
418 | */ | |
419 | 0 | public JSONObject(Object object, String names[]) |
420 | { | |
421 | 0 | this(names.length); |
422 | 0 | Class<?> c = object.getClass(); |
423 | 0 | for (int i = 0; i < names.length; i += 1) |
424 | { | |
425 | 0 | String name = names[i]; |
426 | 0 | try |
427 | { | |
428 | 0 | this.putOpt(name, c.getField(name).get(object)); |
429 | } catch (Exception ignore) | |
430 | { | |
431 | } | |
432 | } | |
433 | } | |
434 | ||
435 | /** | |
436 | * Construct a JSONObject from a source JSON text string. This is the most | |
437 | * commonly used JSONObject constructor. | |
438 | * | |
439 | * @param source | |
440 | * A string beginning with <code>{</code> <small>(left | |
441 | * brace)</small> and ending with <code>}</code> <small>(right | |
442 | * brace)</small>. | |
443 | * @exception JSONException | |
444 | * If there is a syntax error in the source string or a | |
445 | * duplicated key. | |
446 | */ | |
447 | 0 | public JSONObject(String source) throws JSONException |
448 | { | |
449 | 0 | this(new JSONTokener(source)); |
450 | } | |
451 | ||
452 | /** | |
453 | * Construct a JSONObject from a ResourceBundle. | |
454 | * | |
455 | * @param baseName | |
456 | * The ResourceBundle base name. | |
457 | * @param locale | |
458 | * The Locale to load the ResourceBundle for. | |
459 | * @throws JSONException | |
460 | * If any JSONExceptions are detected. | |
461 | */ | |
462 | 0 | public JSONObject(String baseName, Locale locale) throws JSONException |
463 | { | |
464 | 0 | this(); |
465 | 0 | ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, |
466 | Thread.currentThread().getContextClassLoader()); | |
467 | ||
468 | // Iterate through the keys in the bundle. | |
469 | ||
470 | 0 | Enumeration<String> keys = bundle.getKeys(); |
471 | 0 | while (keys.hasMoreElements()) |
472 | { | |
473 | 0 | Object key = keys.nextElement(); |
474 | 0 | if (key != null) |
475 | { | |
476 | ||
477 | // Go through the path, ensuring that there is a nested JSONObject for | |
478 | // each | |
479 | // segment except the last. Add the value using the last segment's name | |
480 | // into | |
481 | // the deepest nested JSONObject. | |
482 | ||
483 | 0 | String[] path = ((String) key).split("\\."); |
484 | 0 | int last = path.length - 1; |
485 | 0 | JSONObject target = this; |
486 | 0 | for (int i = 0; i < last; i += 1) |
487 | { | |
488 | 0 | String segment = path[i]; |
489 | 0 | JSONObject nextTarget = target.optJSONObject(segment); |
490 | 0 | if (nextTarget == null) |
491 | { | |
492 | 0 | nextTarget = new JSONObject(); |
493 | 0 | target.put(segment, nextTarget); |
494 | } | |
495 | 0 | target = nextTarget; |
496 | } | |
497 | 0 | target.put(path[last], bundle.getString((String) key)); |
498 | } | |
499 | } | |
500 | } | |
501 | ||
502 | /** | |
503 | * Constructor to specify an initial capacity of the internal map. Useful for | |
504 | * library internal calls where we know, or at least can best guess, how big | |
505 | * this JSONObject will be. | |
506 | * | |
507 | * @param initialCapacity | |
508 | * initial capacity of the internal map. | |
509 | */ | |
510 | 0 | protected JSONObject(int initialCapacity) |
511 | { | |
512 | 0 | this.map = new HashMap<String, Object>(initialCapacity); |
513 | } | |
514 | ||
515 | /** | |
516 | * Accumulate values under a key. It is similar to the put method except that | |
517 | * if there is already an object stored under the key then a JSONArray is | |
518 | * stored under the key to hold all of the accumulated values. If there is | |
519 | * already a JSONArray, then the new value is appended to it. In contrast, the | |
520 | * put method replaces the previous value. | |
521 | * | |
522 | * If only one value is accumulated that is not a JSONArray, then the result | |
523 | * will be the same as using put. But if multiple values are accumulated, then | |
524 | * the result will be like append. | |
525 | * | |
526 | * @param key | |
527 | * A key string. | |
528 | * @param value | |
529 | * An object to be accumulated under the key. | |
530 | * @return this. | |
531 | * @throws JSONException | |
532 | * If the value is non-finite number. | |
533 | * @throws NullPointerException | |
534 | * If the key is <code>null</code>. | |
535 | */ | |
536 | 0 | public JSONObject accumulate(String key, Object value) |
537 | throws JSONException | |
538 | { | |
539 | 0 | testValidity(value); |
540 | 0 | Object object = this.opt(key); |
541 | 0 | if (object == null) |
542 | { | |
543 | 0 | this.put(key, value instanceof JSONArray ? new JSONArray().put(value) |
544 | : value); | |
545 | } | |
546 | 0 | else if (object instanceof JSONArray) |
547 | { | |
548 | 0 | ((JSONArray) object).put(value); |
549 | } | |
550 | else | |
551 | { | |
552 | 0 | this.put(key, new JSONArray().put(object).put(value)); |
553 | } | |
554 | 0 | return this; |
555 | } | |
556 | ||
557 | /** | |
558 | * Append values to the array under a key. If the key does not exist in the | |
559 | * JSONObject, then the key is put in the JSONObject with its value being a | |
560 | * JSONArray containing the value parameter. If the key was already associated | |
561 | * with a JSONArray, then the value parameter is appended to it. | |
562 | * | |
563 | * @param key | |
564 | * A key string. | |
565 | * @param value | |
566 | * An object to be accumulated under the key. | |
567 | * @return this. | |
568 | * @throws JSONException | |
569 | * If the value is non-finite number or if the current value | |
570 | * associated with the key is not a JSONArray. | |
571 | * @throws NullPointerException | |
572 | * If the key is <code>null</code>. | |
573 | */ | |
574 | 0 | public JSONObject append(String key, Object value) throws JSONException |
575 | { | |
576 | 0 | testValidity(value); |
577 | 0 | Object object = this.opt(key); |
578 | 0 | if (object == null) |
579 | { | |
580 | 0 | this.put(key, new JSONArray().put(value)); |
581 | } | |
582 | 0 | else if (object instanceof JSONArray) |
583 | { | |
584 | 0 | this.put(key, ((JSONArray) object).put(value)); |
585 | } | |
586 | else | |
587 | { | |
588 | 0 | throw new JSONException( |
589 | "JSONObject[" + key + "] is not a JSONArray."); | |
590 | } | |
591 | 0 | return this; |
592 | } | |
593 | ||
594 | /** | |
595 | * Produce a string from a double. The string "null" will be returned if the | |
596 | * number is not finite. | |
597 | * | |
598 | * @param d | |
599 | * A double. | |
600 | * @return A String. | |
601 | */ | |
602 | 0 | public static String doubleToString(double d) |
603 | { | |
604 | 0 | if (Double.isInfinite(d) || Double.isNaN(d)) |
605 | { | |
606 | 0 | return "null"; |
607 | } | |
608 | ||
609 | // Shave off trailing zeros and decimal point, if possible. | |
610 | ||
611 | 0 | String string = Double.toString(d); |
612 | 0 | if (string.indexOf('.') > 0 && string.indexOf('e') < 0 |
613 | && string.indexOf('E') < 0) | |
614 | { | |
615 | 0 | while (string.endsWith("0")) |
616 | { | |
617 | 0 | string = string.substring(0, string.length() - 1); |
618 | } | |
619 | 0 | if (string.endsWith(".")) |
620 | { | |
621 | 0 | string = string.substring(0, string.length() - 1); |
622 | } | |
623 | } | |
624 | 0 | return string; |
625 | } | |
626 | ||
627 | /** | |
628 | * Get the value object associated with a key. | |
629 | * | |
630 | * @param key | |
631 | * A key string. | |
632 | * @return The object associated with the key. | |
633 | * @throws JSONException | |
634 | * if the key is not found. | |
635 | */ | |
636 | 0 | public Object get(String key) throws JSONException |
637 | { | |
638 | 0 | if (key == null) |
639 | { | |
640 | 0 | throw new JSONException("Null key."); |
641 | } | |
642 | 0 | Object object = this.opt(key); |
643 | 0 | if (object == null) |
644 | { | |
645 | 0 | throw new JSONException("JSONObject[" + quote(key) + "] not found."); |
646 | } | |
647 | 0 | return object; |
648 | } | |
649 | ||
650 | /** | |
651 | * Get the enum value associated with a key. | |
652 | * | |
653 | * @param clazz | |
654 | * The type of enum to retrieve. | |
655 | * @param key | |
656 | * A key string. | |
657 | * @return The enum value associated with the key | |
658 | * @throws JSONException | |
659 | * if the key is not found or if the value cannot be converted to an | |
660 | * enum. | |
661 | */ | |
662 | 0 | public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) |
663 | throws JSONException | |
664 | { | |
665 | 0 | E val = optEnum(clazz, key); |
666 | 0 | if (val == null) |
667 | { | |
668 | // JSONException should really take a throwable argument. | |
669 | // If it did, I would re-implement this with the Enum.valueOf | |
670 | // method and place any thrown exception in the JSONException | |
671 | 0 | throw new JSONException( |
672 | "JSONObject[" + quote(key) + "] is not an enum of type " | |
673 | + quote(clazz.getSimpleName()) + "."); | |
674 | } | |
675 | 0 | return val; |
676 | } | |
677 | ||
678 | /** | |
679 | * Get the boolean value associated with a key. | |
680 | * | |
681 | * @param key | |
682 | * A key string. | |
683 | * @return The truth. | |
684 | * @throws JSONException | |
685 | * if the value is not a Boolean or the String "true" or "false". | |
686 | */ | |
687 | 0 | public boolean getBoolean(String key) throws JSONException |
688 | { | |
689 | 0 | Object object = this.get(key); |
690 | 0 | if (object.equals(Boolean.FALSE) || (object instanceof String |
691 | && ((String) object).equalsIgnoreCase("false"))) | |
692 | { | |
693 | 0 | return false; |
694 | } | |
695 | 0 | else if (object.equals(Boolean.TRUE) || (object instanceof String |
696 | && ((String) object).equalsIgnoreCase("true"))) | |
697 | { | |
698 | 0 | return true; |
699 | } | |
700 | 0 | throw new JSONException( |
701 | "JSONObject[" + quote(key) + "] is not a Boolean."); | |
702 | } | |
703 | ||
704 | /** | |
705 | * Get the BigInteger value associated with a key. | |
706 | * | |
707 | * @param key | |
708 | * A key string. | |
709 | * @return The numeric value. | |
710 | * @throws JSONException | |
711 | * if the key is not found or if the value cannot be converted to | |
712 | * BigInteger. | |
713 | */ | |
714 | 0 | public BigInteger getBigInteger(String key) throws JSONException |
715 | { | |
716 | 0 | Object object = this.get(key); |
717 | 0 | try |
718 | { | |
719 | 0 | return new BigInteger(object.toString()); |
720 | } catch (Exception e) | |
721 | { | |
722 | 0 | throw new JSONException("JSONObject[" + quote(key) |
723 | + "] could not be converted to BigInteger.", e); | |
724 | } | |
725 | } | |
726 | ||
727 | /** | |
728 | * Get the BigDecimal value associated with a key. | |
729 | * | |
730 | * @param key | |
731 | * A key string. | |
732 | * @return The numeric value. | |
733 | * @throws JSONException | |
734 | * if the key is not found or if the value cannot be converted to | |
735 | * BigDecimal. | |
736 | */ | |
737 | 0 | public BigDecimal getBigDecimal(String key) throws JSONException |
738 | { | |
739 | 0 | Object object = this.get(key); |
740 | 0 | if (object instanceof BigDecimal) |
741 | { | |
742 | 0 | return (BigDecimal) object; |
743 | } | |
744 | 0 | try |
745 | { | |
746 | 0 | return new BigDecimal(object.toString()); |
747 | } catch (Exception e) | |
748 | { | |
749 | 0 | throw new JSONException("JSONObject[" + quote(key) |
750 | + "] could not be converted to BigDecimal.", e); | |
751 | } | |
752 | } | |
753 | ||
754 | /** | |
755 | * Get the double value associated with a key. | |
756 | * | |
757 | * @param key | |
758 | * A key string. | |
759 | * @return The numeric value. | |
760 | * @throws JSONException | |
761 | * if the key is not found or if the value is not a Number object | |
762 | * and cannot be converted to a number. | |
763 | */ | |
764 | 0 | public double getDouble(String key) throws JSONException |
765 | { | |
766 | 0 | Object object = this.get(key); |
767 | 0 | try |
768 | { | |
769 | 0 | return object instanceof Number ? ((Number) object).doubleValue() |
770 | : Double.parseDouble(object.toString()); | |
771 | } catch (Exception e) | |
772 | { | |
773 | 0 | throw new JSONException( |
774 | "JSONObject[" + quote(key) + "] is not a number.", e); | |
775 | } | |
776 | } | |
777 | ||
778 | /** | |
779 | * Get the float value associated with a key. | |
780 | * | |
781 | * @param key | |
782 | * A key string. | |
783 | * @return The numeric value. | |
784 | * @throws JSONException | |
785 | * if the key is not found or if the value is not a Number object | |
786 | * and cannot be converted to a number. | |
787 | */ | |
788 | 0 | public float getFloat(String key) throws JSONException |
789 | { | |
790 | 0 | Object object = this.get(key); |
791 | 0 | try |
792 | { | |
793 | 0 | return object instanceof Number ? ((Number) object).floatValue() |
794 | : Float.parseFloat(object.toString()); | |
795 | } catch (Exception e) | |
796 | { | |
797 | 0 | throw new JSONException( |
798 | "JSONObject[" + quote(key) + "] is not a number.", e); | |
799 | } | |
800 | } | |
801 | ||
802 | /** | |
803 | * Get the Number value associated with a key. | |
804 | * | |
805 | * @param key | |
806 | * A key string. | |
807 | * @return The numeric value. | |
808 | * @throws JSONException | |
809 | * if the key is not found or if the value is not a Number object | |
810 | * and cannot be converted to a number. | |
811 | */ | |
812 | 0 | public Number getNumber(String key) throws JSONException |
813 | { | |
814 | 0 | Object object = this.get(key); |
815 | 0 | try |
816 | { | |
817 | 0 | if (object instanceof Number) |
818 | { | |
819 | 0 | return (Number) object; |
820 | } | |
821 | 0 | return stringToNumber(object.toString()); |
822 | } catch (Exception e) | |
823 | { | |
824 | 0 | throw new JSONException( |
825 | "JSONObject[" + quote(key) + "] is not a number.", e); | |
826 | } | |
827 | } | |
828 | ||
829 | /** | |
830 | * Get the int value associated with a key. | |
831 | * | |
832 | * @param key | |
833 | * A key string. | |
834 | * @return The integer value. | |
835 | * @throws JSONException | |
836 | * if the key is not found or if the value cannot be converted to an | |
837 | * integer. | |
838 | */ | |
839 | 0 | public int getInt(String key) throws JSONException |
840 | { | |
841 | 0 | Object object = this.get(key); |
842 | 0 | try |
843 | { | |
844 | 0 | return object instanceof Number ? ((Number) object).intValue() |
845 | : Integer.parseInt((String) object); | |
846 | } catch (Exception e) | |
847 | { | |
848 | 0 | throw new JSONException( |
849 | "JSONObject[" + quote(key) + "] is not an int.", e); | |
850 | } | |
851 | } | |
852 | ||
853 | /** | |
854 | * Get the JSONArray value associated with a key. | |
855 | * | |
856 | * @param key | |
857 | * A key string. | |
858 | * @return A JSONArray which is the value. | |
859 | * @throws JSONException | |
860 | * if the key is not found or if the value is not a JSONArray. | |
861 | */ | |
862 | 0 | public JSONArray getJSONArray(String key) throws JSONException |
863 | { | |
864 | 0 | Object object = this.get(key); |
865 | 0 | if (object instanceof JSONArray) |
866 | { | |
867 | 0 | return (JSONArray) object; |
868 | } | |
869 | 0 | throw new JSONException( |
870 | "JSONObject[" + quote(key) + "] is not a JSONArray."); | |
871 | } | |
872 | ||
873 | /** | |
874 | * Get the JSONObject value associated with a key. | |
875 | * | |
876 | * @param key | |
877 | * A key string. | |
878 | * @return A JSONObject which is the value. | |
879 | * @throws JSONException | |
880 | * if the key is not found or if the value is not a JSONObject. | |
881 | */ | |
882 | 0 | public JSONObject getJSONObject(String key) throws JSONException |
883 | { | |
884 | 0 | Object object = this.get(key); |
885 | 0 | if (object instanceof JSONObject) |
886 | { | |
887 | 0 | return (JSONObject) object; |
888 | } | |
889 | 0 | throw new JSONException( |
890 | "JSONObject[" + quote(key) + "] is not a JSONObject."); | |
891 | } | |
892 | ||
893 | /** | |
894 | * Get the long value associated with a key. | |
895 | * | |
896 | * @param key | |
897 | * A key string. | |
898 | * @return The long value. | |
899 | * @throws JSONException | |
900 | * if the key is not found or if the value cannot be converted to a | |
901 | * long. | |
902 | */ | |
903 | 0 | public long getLong(String key) throws JSONException |
904 | { | |
905 | 0 | Object object = this.get(key); |
906 | 0 | try |
907 | { | |
908 | 0 | return object instanceof Number ? ((Number) object).longValue() |
909 | : Long.parseLong((String) object); | |
910 | } catch (Exception e) | |
911 | { | |
912 | 0 | throw new JSONException( |
913 | "JSONObject[" + quote(key) + "] is not a long.", e); | |
914 | } | |
915 | } | |
916 | ||
917 | /** | |
918 | * Get an array of field names from a JSONObject. | |
919 | * | |
920 | * @return An array of field names, or null if there are no names. | |
921 | */ | |
922 | 0 | public static String[] getNames(JSONObject jo) |
923 | { | |
924 | 0 | if (jo.isEmpty()) |
925 | { | |
926 | 0 | return null; |
927 | } | |
928 | 0 | return jo.keySet().toArray(new String[jo.length()]); |
929 | } | |
930 | ||
931 | /** | |
932 | * Get an array of field names from an Object. | |
933 | * | |
934 | * @return An array of field names, or null if there are no names. | |
935 | */ | |
936 | 0 | public static String[] getNames(Object object) |
937 | { | |
938 | 0 | if (object == null) |
939 | { | |
940 | 0 | return null; |
941 | } | |
942 | 0 | Class<?> klass = object.getClass(); |
943 | 0 | Field[] fields = klass.getFields(); |
944 | 0 | int length = fields.length; |
945 | 0 | if (length == 0) |
946 | { | |
947 | 0 | return null; |
948 | } | |
949 | 0 | String[] names = new String[length]; |
950 | 0 | for (int i = 0; i < length; i += 1) |
951 | { | |
952 | 0 | names[i] = fields[i].getName(); |
953 | } | |
954 | 0 | return names; |
955 | } | |
956 | ||
957 | /** | |
958 | * Get the string associated with a key. | |
959 | * | |
960 | * @param key | |
961 | * A key string. | |
962 | * @return A string which is the value. | |
963 | * @throws JSONException | |
964 | * if there is no string value for the key. | |
965 | */ | |
966 | 0 | public String getString(String key) throws JSONException |
967 | { | |
968 | 0 | Object object = this.get(key); |
969 | 0 | if (object instanceof String) |
970 | { | |
971 | 0 | return (String) object; |
972 | } | |
973 | 0 | throw new JSONException("JSONObject[" + quote(key) + "] not a string."); |
974 | } | |
975 | ||
976 | /** | |
977 | * Determine if the JSONObject contains a specific key. | |
978 | * | |
979 | * @param key | |
980 | * A key string. | |
981 | * @return true if the key exists in the JSONObject. | |
982 | */ | |
983 | 0 | public boolean has(String key) |
984 | { | |
985 | 0 | return this.map.containsKey(key); |
986 | } | |
987 | ||
988 | /** | |
989 | * Increment a property of a JSONObject. If there is no such property, create | |
990 | * one with a value of 1. If there is such a property, and if it is an | |
991 | * Integer, Long, Double, or Float, then add one to it. | |
992 | * | |
993 | * @param key | |
994 | * A key string. | |
995 | * @return this. | |
996 | * @throws JSONException | |
997 | * If there is already a property with this name that is not an | |
998 | * Integer, Long, Double, or Float. | |
999 | */ | |
1000 | 0 | public JSONObject increment(String key) throws JSONException |
1001 | { | |
1002 | 0 | Object value = this.opt(key); |
1003 | 0 | if (value == null) |
1004 | { | |
1005 | 0 | this.put(key, 1); |
1006 | } | |
1007 | 0 | else if (value instanceof BigInteger) |
1008 | { | |
1009 | 0 | this.put(key, ((BigInteger) value).add(BigInteger.ONE)); |
1010 | } | |
1011 | 0 | else if (value instanceof BigDecimal) |
1012 | { | |
1013 | 0 | this.put(key, ((BigDecimal) value).add(BigDecimal.ONE)); |
1014 | } | |
1015 | 0 | else if (value instanceof Integer) |
1016 | { | |
1017 | 0 | this.put(key, ((Integer) value).intValue() + 1); |
1018 | } | |
1019 | 0 | else if (value instanceof Long) |
1020 | { | |
1021 | 0 | this.put(key, ((Long) value).longValue() + 1L); |
1022 | } | |
1023 | 0 | else if (value instanceof Double) |
1024 | { | |
1025 | 0 | this.put(key, ((Double) value).doubleValue() + 1.0d); |
1026 | } | |
1027 | 0 | else if (value instanceof Float) |
1028 | { | |
1029 | 0 | this.put(key, ((Float) value).floatValue() + 1.0f); |
1030 | } | |
1031 | else | |
1032 | { | |
1033 | 0 | throw new JSONException("Unable to increment [" + quote(key) + "]."); |
1034 | } | |
1035 | 0 | return this; |
1036 | } | |
1037 | ||
1038 | /** | |
1039 | * Determine if the value associated with the key is <code>null</code> or if | |
1040 | * there is no value. | |
1041 | * | |
1042 | * @param key | |
1043 | * A key string. | |
1044 | * @return true if there is no value associated with the key or if the value | |
1045 | * is the JSONObject.NULL object. | |
1046 | */ | |
1047 | 0 | public boolean isNull(String key) |
1048 | { | |
1049 | 0 | return JSONObject.NULL.equals(this.opt(key)); |
1050 | } | |
1051 | ||
1052 | /** | |
1053 | * Get an enumeration of the keys of the JSONObject. Modifying this key Set | |
1054 | * will also modify the JSONObject. Use with caution. | |
1055 | * | |
1056 | * @see Set#iterator() | |
1057 | * | |
1058 | * @return An iterator of the keys. | |
1059 | */ | |
1060 | 0 | public Iterator<String> keys() |
1061 | { | |
1062 | 0 | return this.keySet().iterator(); |
1063 | } | |
1064 | ||
1065 | /** | |
1066 | * Get a set of keys of the JSONObject. Modifying this key Set will also | |
1067 | * modify the JSONObject. Use with caution. | |
1068 | * | |
1069 | * @see Map#keySet() | |
1070 | * | |
1071 | * @return A keySet. | |
1072 | */ | |
1073 | 0 | public Set<String> keySet() |
1074 | { | |
1075 | 0 | return this.map.keySet(); |
1076 | } | |
1077 | ||
1078 | /** | |
1079 | * Get a set of entries of the JSONObject. These are raw values and may not | |
1080 | * match what is returned by the JSONObject get* and opt* functions. Modifying | |
1081 | * the returned EntrySet or the Entry objects contained therein will modify | |
1082 | * the backing JSONObject. This does not return a clone or a read-only view. | |
1083 | * | |
1084 | * Use with caution. | |
1085 | * | |
1086 | * @see Map#entrySet() | |
1087 | * | |
1088 | * @return An Entry Set | |
1089 | */ | |
1090 | 291 | protected Set<Entry<String, Object>> entrySet() |
1091 | { | |
1092 | 291 | return this.map.entrySet(); |
1093 | } | |
1094 | ||
1095 | /** | |
1096 | * Get the number of keys stored in the JSONObject. | |
1097 | * | |
1098 | * @return The number of keys in the JSONObject. | |
1099 | */ | |
1100 | 291 | public int length() |
1101 | { | |
1102 | 291 | return this.map.size(); |
1103 | } | |
1104 | ||
1105 | /** | |
1106 | * Check if JSONObject is empty. | |
1107 | * | |
1108 | * @return true if JSONObject is empty, otherwise false. | |
1109 | */ | |
1110 | 0 | public boolean isEmpty() |
1111 | { | |
1112 | 0 | return map.isEmpty(); |
1113 | } | |
1114 | ||
1115 | /** | |
1116 | * Produce a JSONArray containing the names of the elements of this | |
1117 | * JSONObject. | |
1118 | * | |
1119 | * @return A JSONArray containing the key strings, or null if the JSONObject | |
1120 | * is empty. | |
1121 | */ | |
1122 | 0 | public JSONArray names() |
1123 | { | |
1124 | 0 | if (this.map.isEmpty()) |
1125 | { | |
1126 | 0 | return null; |
1127 | } | |
1128 | 0 | return new JSONArray(this.map.keySet()); |
1129 | } | |
1130 | ||
1131 | /** | |
1132 | * Produce a string from a Number. | |
1133 | * | |
1134 | * @param number | |
1135 | * A Number | |
1136 | * @return A String. | |
1137 | * @throws JSONException | |
1138 | * If n is a non-finite number. | |
1139 | */ | |
1140 | 449 | public static String numberToString(Number number) throws JSONException |
1141 | { | |
1142 | 449 | if (number == null) |
1143 | { | |
1144 | 0 | throw new JSONException("Null pointer"); |
1145 | } | |
1146 | 449 | testValidity(number); |
1147 | ||
1148 | // Shave off trailing zeros and decimal point, if possible. | |
1149 | ||
1150 | 449 | String string = number.toString(); |
1151 | 449 | if (string.indexOf('.') > 0 && string.indexOf('e') < 0 |
1152 | && string.indexOf('E') < 0) | |
1153 | { | |
1154 | 408 | while (string.endsWith("0")) |
1155 | { | |
1156 | 204 | string = string.substring(0, string.length() - 1); |
1157 | } | |
1158 | 204 | if (string.endsWith(".")) |
1159 | { | |
1160 | 204 | string = string.substring(0, string.length() - 1); |
1161 | } | |
1162 | } | |
1163 | 449 | return string; |
1164 | } | |
1165 | ||
1166 | /** | |
1167 | * Get an optional value associated with a key. | |
1168 | * | |
1169 | * @param key | |
1170 | * A key string. | |
1171 | * @return An object which is the value, or null if there is no value. | |
1172 | */ | |
1173 | 0 | public Object opt(String key) |
1174 | { | |
1175 | 0 | return key == null ? null : this.map.get(key); |
1176 | } | |
1177 | ||
1178 | /** | |
1179 | * Get the enum value associated with a key. | |
1180 | * | |
1181 | * @param clazz | |
1182 | * The type of enum to retrieve. | |
1183 | * @param key | |
1184 | * A key string. | |
1185 | * @return The enum value associated with the key or null if not found | |
1186 | */ | |
1187 | 0 | public <E extends Enum<E>> E optEnum(Class<E> clazz, String key) |
1188 | { | |
1189 | 0 | return this.optEnum(clazz, key, null); |
1190 | } | |
1191 | ||
1192 | /** | |
1193 | * Get the enum value associated with a key. | |
1194 | * | |
1195 | * @param clazz | |
1196 | * The type of enum to retrieve. | |
1197 | * @param key | |
1198 | * A key string. | |
1199 | * @param defaultValue | |
1200 | * The default in case the value is not found | |
1201 | * @return The enum value associated with the key or defaultValue if the value | |
1202 | * is not found or cannot be assigned to <code>clazz</code> | |
1203 | */ | |
1204 | 0 | public <E extends Enum<E>> E optEnum(Class<E> clazz, String key, |
1205 | E defaultValue) | |
1206 | { | |
1207 | 0 | try |
1208 | { | |
1209 | 0 | Object val = this.opt(key); |
1210 | 0 | if (NULL.equals(val)) |
1211 | { | |
1212 | 0 | return defaultValue; |
1213 | } | |
1214 | 0 | if (clazz.isAssignableFrom(val.getClass())) |
1215 | { | |
1216 | // we just checked it! | |
1217 | 0 | @SuppressWarnings("unchecked") |
1218 | E myE = (E) val; | |
1219 | 0 | return myE; |
1220 | } | |
1221 | 0 | return Enum.valueOf(clazz, val.toString()); |
1222 | } catch (IllegalArgumentException e) | |
1223 | { | |
1224 | 0 | return defaultValue; |
1225 | } catch (NullPointerException e) | |
1226 | { | |
1227 | 0 | return defaultValue; |
1228 | } | |
1229 | } | |
1230 | ||
1231 | /** | |
1232 | * Get an optional boolean associated with a key. It returns false if there is | |
1233 | * no such key, or if the value is not Boolean.TRUE or the String "true". | |
1234 | * | |
1235 | * @param key | |
1236 | * A key string. | |
1237 | * @return The truth. | |
1238 | */ | |
1239 | 0 | public boolean optBoolean(String key) |
1240 | { | |
1241 | 0 | return this.optBoolean(key, false); |
1242 | } | |
1243 | ||
1244 | /** | |
1245 | * Get an optional boolean associated with a key. It returns the defaultValue | |
1246 | * if there is no such key, or if it is not a Boolean or the String "true" or | |
1247 | * "false" (case insensitive). | |
1248 | * | |
1249 | * @param key | |
1250 | * A key string. | |
1251 | * @param defaultValue | |
1252 | * The default. | |
1253 | * @return The truth. | |
1254 | */ | |
1255 | 0 | public boolean optBoolean(String key, boolean defaultValue) |
1256 | { | |
1257 | 0 | Object val = this.opt(key); |
1258 | 0 | if (NULL.equals(val)) |
1259 | { | |
1260 | 0 | return defaultValue; |
1261 | } | |
1262 | 0 | if (val instanceof Boolean) |
1263 | { | |
1264 | 0 | return ((Boolean) val).booleanValue(); |
1265 | } | |
1266 | 0 | try |
1267 | { | |
1268 | // we'll use the get anyway because it does string conversion. | |
1269 | 0 | return this.getBoolean(key); |
1270 | } catch (Exception e) | |
1271 | { | |
1272 | 0 | return defaultValue; |
1273 | } | |
1274 | } | |
1275 | ||
1276 | /** | |
1277 | * Get an optional BigDecimal associated with a key, or the defaultValue if | |
1278 | * there is no such key or if its value is not a number. If the value is a | |
1279 | * string, an attempt will be made to evaluate it as a number. | |
1280 | * | |
1281 | * @param key | |
1282 | * A key string. | |
1283 | * @param defaultValue | |
1284 | * The default. | |
1285 | * @return An object which is the value. | |
1286 | */ | |
1287 | 0 | public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) |
1288 | { | |
1289 | 0 | Object val = this.opt(key); |
1290 | 0 | if (NULL.equals(val)) |
1291 | { | |
1292 | 0 | return defaultValue; |
1293 | } | |
1294 | 0 | if (val instanceof BigDecimal) |
1295 | { | |
1296 | 0 | return (BigDecimal) val; |
1297 | } | |
1298 | 0 | if (val instanceof BigInteger) |
1299 | { | |
1300 | 0 | return new BigDecimal((BigInteger) val); |
1301 | } | |
1302 | 0 | if (val instanceof Double || val instanceof Float) |
1303 | { | |
1304 | 0 | return new BigDecimal(((Number) val).doubleValue()); |
1305 | } | |
1306 | 0 | if (val instanceof Long || val instanceof Integer |
1307 | || val instanceof Short || val instanceof Byte) | |
1308 | { | |
1309 | 0 | return new BigDecimal(((Number) val).longValue()); |
1310 | } | |
1311 | // don't check if it's a string in case of unchecked Number subclasses | |
1312 | 0 | try |
1313 | { | |
1314 | 0 | return new BigDecimal(val.toString()); |
1315 | } catch (Exception e) | |
1316 | { | |
1317 | 0 | return defaultValue; |
1318 | } | |
1319 | } | |
1320 | ||
1321 | /** | |
1322 | * Get an optional BigInteger associated with a key, or the defaultValue if | |
1323 | * there is no such key or if its value is not a number. If the value is a | |
1324 | * string, an attempt will be made to evaluate it as a number. | |
1325 | * | |
1326 | * @param key | |
1327 | * A key string. | |
1328 | * @param defaultValue | |
1329 | * The default. | |
1330 | * @return An object which is the value. | |
1331 | */ | |
1332 | 0 | public BigInteger optBigInteger(String key, BigInteger defaultValue) |
1333 | { | |
1334 | 0 | Object val = this.opt(key); |
1335 | 0 | if (NULL.equals(val)) |
1336 | { | |
1337 | 0 | return defaultValue; |
1338 | } | |
1339 | 0 | if (val instanceof BigInteger) |
1340 | { | |
1341 | 0 | return (BigInteger) val; |
1342 | } | |
1343 | 0 | if (val instanceof BigDecimal) |
1344 | { | |
1345 | 0 | return ((BigDecimal) val).toBigInteger(); |
1346 | } | |
1347 | 0 | if (val instanceof Double || val instanceof Float) |
1348 | { | |
1349 | 0 | return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); |
1350 | } | |
1351 | 0 | if (val instanceof Long || val instanceof Integer |
1352 | || val instanceof Short || val instanceof Byte) | |
1353 | { | |
1354 | 0 | return BigInteger.valueOf(((Number) val).longValue()); |
1355 | } | |
1356 | // don't check if it's a string in case of unchecked Number subclasses | |
1357 | 0 | try |
1358 | { | |
1359 | // the other opt functions handle implicit conversions, i.e. | |
1360 | // jo.put("double",1.1d); | |
1361 | // jo.optInt("double"); -- will return 1, not an error | |
1362 | // this conversion to BigDecimal then to BigInteger is to maintain | |
1363 | // that type cast support that may truncate the decimal. | |
1364 | 0 | final String valStr = val.toString(); |
1365 | 0 | if (isDecimalNotation(valStr)) |
1366 | { | |
1367 | 0 | return new BigDecimal(valStr).toBigInteger(); |
1368 | } | |
1369 | 0 | return new BigInteger(valStr); |
1370 | } catch (Exception e) | |
1371 | { | |
1372 | 0 | return defaultValue; |
1373 | } | |
1374 | } | |
1375 | ||
1376 | /** | |
1377 | * Get an optional double associated with a key, or NaN if there is no such | |
1378 | * key or if its value is not a number. If the value is a string, an attempt | |
1379 | * will be made to evaluate it as a number. | |
1380 | * | |
1381 | * @param key | |
1382 | * A string which is the key. | |
1383 | * @return An object which is the value. | |
1384 | */ | |
1385 | 0 | public double optDouble(String key) |
1386 | { | |
1387 | 0 | return this.optDouble(key, Double.NaN); |
1388 | } | |
1389 | ||
1390 | /** | |
1391 | * Get an optional double associated with a key, or the defaultValue if there | |
1392 | * is no such key or if its value is not a number. If the value is a string, | |
1393 | * an attempt will be made to evaluate it as a number. | |
1394 | * | |
1395 | * @param key | |
1396 | * A key string. | |
1397 | * @param defaultValue | |
1398 | * The default. | |
1399 | * @return An object which is the value. | |
1400 | */ | |
1401 | 0 | public double optDouble(String key, double defaultValue) |
1402 | { | |
1403 | 0 | Object val = this.opt(key); |
1404 | 0 | if (NULL.equals(val)) |
1405 | { | |
1406 | 0 | return defaultValue; |
1407 | } | |
1408 | 0 | if (val instanceof Number) |
1409 | { | |
1410 | 0 | return ((Number) val).doubleValue(); |
1411 | } | |
1412 | 0 | if (val instanceof String) |
1413 | { | |
1414 | 0 | try |
1415 | { | |
1416 | 0 | return Double.parseDouble((String) val); |
1417 | } catch (Exception e) | |
1418 | { | |
1419 | 0 | return defaultValue; |
1420 | } | |
1421 | } | |
1422 | 0 | return defaultValue; |
1423 | } | |
1424 | ||
1425 | /** | |
1426 | * Get the optional double value associated with an index. NaN is returned if | |
1427 | * there is no value for the index, or if the value is not a number and cannot | |
1428 | * be converted to a number. | |
1429 | * | |
1430 | * @param key | |
1431 | * A key string. | |
1432 | * @return The value. | |
1433 | */ | |
1434 | 0 | public float optFloat(String key) |
1435 | { | |
1436 | 0 | return this.optFloat(key, Float.NaN); |
1437 | } | |
1438 | ||
1439 | /** | |
1440 | * Get the optional double value associated with an index. The defaultValue is | |
1441 | * returned if there is no value for the index, or if the value is not a | |
1442 | * number and cannot be converted to a number. | |
1443 | * | |
1444 | * @param key | |
1445 | * A key string. | |
1446 | * @param defaultValue | |
1447 | * The default value. | |
1448 | * @return The value. | |
1449 | */ | |
1450 | 0 | public float optFloat(String key, float defaultValue) |
1451 | { | |
1452 | 0 | Object val = this.opt(key); |
1453 | 0 | if (JSONObject.NULL.equals(val)) |
1454 | { | |
1455 | 0 | return defaultValue; |
1456 | } | |
1457 | 0 | if (val instanceof Number) |
1458 | { | |
1459 | 0 | return ((Number) val).floatValue(); |
1460 | } | |
1461 | 0 | if (val instanceof String) |
1462 | { | |
1463 | 0 | try |
1464 | { | |
1465 | 0 | return Float.parseFloat((String) val); |
1466 | } catch (Exception e) | |
1467 | { | |
1468 | 0 | return defaultValue; |
1469 | } | |
1470 | } | |
1471 | 0 | return defaultValue; |
1472 | } | |
1473 | ||
1474 | /** | |
1475 | * Get an optional int value associated with a key, or zero if there is no | |
1476 | * such key or if the value is not a number. If the value is a string, an | |
1477 | * attempt will be made to evaluate it as a number. | |
1478 | * | |
1479 | * @param key | |
1480 | * A key string. | |
1481 | * @return An object which is the value. | |
1482 | */ | |
1483 | 0 | public int optInt(String key) |
1484 | { | |
1485 | 0 | return this.optInt(key, 0); |
1486 | } | |
1487 | ||
1488 | /** | |
1489 | * Get an optional int value associated with a key, or the default if there is | |
1490 | * no such key or if the value is not a number. If the value is a string, an | |
1491 | * attempt will be made to evaluate it as a number. | |
1492 | * | |
1493 | * @param key | |
1494 | * A key string. | |
1495 | * @param defaultValue | |
1496 | * The default. | |
1497 | * @return An object which is the value. | |
1498 | */ | |
1499 | 0 | public int optInt(String key, int defaultValue) |
1500 | { | |
1501 | 0 | Object val = this.opt(key); |
1502 | 0 | if (NULL.equals(val)) |
1503 | { | |
1504 | 0 | return defaultValue; |
1505 | } | |
1506 | 0 | if (val instanceof Number) |
1507 | { | |
1508 | 0 | return ((Number) val).intValue(); |
1509 | } | |
1510 | ||
1511 | 0 | if (val instanceof String) |
1512 | { | |
1513 | 0 | try |
1514 | { | |
1515 | 0 | return new BigDecimal((String) val).intValue(); |
1516 | } catch (Exception e) | |
1517 | { | |
1518 | 0 | return defaultValue; |
1519 | } | |
1520 | } | |
1521 | 0 | return defaultValue; |
1522 | } | |
1523 | ||
1524 | /** | |
1525 | * Get an optional JSONArray associated with a key. It returns null if there | |
1526 | * is no such key, or if its value is not a JSONArray. | |
1527 | * | |
1528 | * @param key | |
1529 | * A key string. | |
1530 | * @return A JSONArray which is the value. | |
1531 | */ | |
1532 | 0 | public JSONArray optJSONArray(String key) |
1533 | { | |
1534 | 0 | Object o = this.opt(key); |
1535 | 0 | return o instanceof JSONArray ? (JSONArray) o : null; |
1536 | } | |
1537 | ||
1538 | /** | |
1539 | * Get an optional JSONObject associated with a key. It returns null if there | |
1540 | * is no such key, or if its value is not a JSONObject. | |
1541 | * | |
1542 | * @param key | |
1543 | * A key string. | |
1544 | * @return A JSONObject which is the value. | |
1545 | */ | |
1546 | 0 | public JSONObject optJSONObject(String key) |
1547 | { | |
1548 | 0 | Object object = this.opt(key); |
1549 | 0 | return object instanceof JSONObject ? (JSONObject) object : null; |
1550 | } | |
1551 | ||
1552 | /** | |
1553 | * Get an optional long value associated with a key, or zero if there is no | |
1554 | * such key or if the value is not a number. If the value is a string, an | |
1555 | * attempt will be made to evaluate it as a number. | |
1556 | * | |
1557 | * @param key | |
1558 | * A key string. | |
1559 | * @return An object which is the value. | |
1560 | */ | |
1561 | 0 | public long optLong(String key) |
1562 | { | |
1563 | 0 | return this.optLong(key, 0); |
1564 | } | |
1565 | ||
1566 | /** | |
1567 | * Get an optional long value associated with a key, or the default if there | |
1568 | * is no such key or if the value is not a number. If the value is a string, | |
1569 | * an attempt will be made to evaluate it as a number. | |
1570 | * | |
1571 | * @param key | |
1572 | * A key string. | |
1573 | * @param defaultValue | |
1574 | * The default. | |
1575 | * @return An object which is the value. | |
1576 | */ | |
1577 | 0 | public long optLong(String key, long defaultValue) |
1578 | { | |
1579 | 0 | Object val = this.opt(key); |
1580 | 0 | if (NULL.equals(val)) |
1581 | { | |
1582 | 0 | return defaultValue; |
1583 | } | |
1584 | 0 | if (val instanceof Number) |
1585 | { | |
1586 | 0 | return ((Number) val).longValue(); |
1587 | } | |
1588 | ||
1589 | 0 | if (val instanceof String) |
1590 | { | |
1591 | 0 | try |
1592 | { | |
1593 | 0 | return new BigDecimal((String) val).longValue(); |
1594 | } catch (Exception e) | |
1595 | { | |
1596 | 0 | return defaultValue; |
1597 | } | |
1598 | } | |
1599 | 0 | return defaultValue; |
1600 | } | |
1601 | ||
1602 | /** | |
1603 | * Get an optional {@link Number} value associated with a key, or | |
1604 | * <code>null</code> if there is no such key or if the value is not a number. | |
1605 | * If the value is a string, an attempt will be made to evaluate it as a | |
1606 | * number ({@link BigDecimal}). This method would be used in cases where type | |
1607 | * coercion of the number value is unwanted. | |
1608 | * | |
1609 | * @param key | |
1610 | * A key string. | |
1611 | * @return An object which is the value. | |
1612 | */ | |
1613 | 0 | public Number optNumber(String key) |
1614 | { | |
1615 | 0 | return this.optNumber(key, null); |
1616 | } | |
1617 | ||
1618 | /** | |
1619 | * Get an optional {@link Number} value associated with a key, or the default | |
1620 | * if there is no such key or if the value is not a number. If the value is a | |
1621 | * string, an attempt will be made to evaluate it as a number. This method | |
1622 | * would be used in cases where type coercion of the number value is unwanted. | |
1623 | * | |
1624 | * @param key | |
1625 | * A key string. | |
1626 | * @param defaultValue | |
1627 | * The default. | |
1628 | * @return An object which is the value. | |
1629 | */ | |
1630 | 0 | public Number optNumber(String key, Number defaultValue) |
1631 | { | |
1632 | 0 | Object val = this.opt(key); |
1633 | 0 | if (NULL.equals(val)) |
1634 | { | |
1635 | 0 | return defaultValue; |
1636 | } | |
1637 | 0 | if (val instanceof Number) |
1638 | { | |
1639 | 0 | return (Number) val; |
1640 | } | |
1641 | ||
1642 | 0 | if (val instanceof String) |
1643 | { | |
1644 | 0 | try |
1645 | { | |
1646 | 0 | return stringToNumber((String) val); |
1647 | } catch (Exception e) | |
1648 | { | |
1649 | 0 | return defaultValue; |
1650 | } | |
1651 | } | |
1652 | 0 | return defaultValue; |
1653 | } | |
1654 | ||
1655 | /** | |
1656 | * Get an optional string associated with a key. It returns an empty string if | |
1657 | * there is no such key. If the value is not a string and is not null, then it | |
1658 | * is converted to a string. | |
1659 | * | |
1660 | * @param key | |
1661 | * A key string. | |
1662 | * @return A string which is the value. | |
1663 | */ | |
1664 | 0 | public String optString(String key) |
1665 | { | |
1666 | 0 | return this.optString(key, ""); |
1667 | } | |
1668 | ||
1669 | /** | |
1670 | * Get an optional string associated with a key. It returns the defaultValue | |
1671 | * if there is no such key. | |
1672 | * | |
1673 | * @param key | |
1674 | * A key string. | |
1675 | * @param defaultValue | |
1676 | * The default. | |
1677 | * @return A string which is the value. | |
1678 | */ | |
1679 | 0 | public String optString(String key, String defaultValue) |
1680 | { | |
1681 | 0 | Object object = this.opt(key); |
1682 | 0 | return NULL.equals(object) ? defaultValue : object.toString(); |
1683 | } | |
1684 | ||
1685 | /** | |
1686 | * Populates the internal map of the JSONObject with the bean properties. The | |
1687 | * bean can not be recursive. | |
1688 | * | |
1689 | * @see JSONObject#JSONObject(Object) | |
1690 | * | |
1691 | * @param bean | |
1692 | * the bean | |
1693 | */ | |
1694 | 283 | private void populateMap(Object bean) |
1695 | { | |
1696 | 283 | Class<?> klass = bean.getClass(); |
1697 | ||
1698 | // If klass is a System class then set includeSuperClass to false. | |
1699 | ||
1700 | 283 | boolean includeSuperClass = klass.getClassLoader() != null; |
1701 | ||
1702 | 283 | Method[] methods = includeSuperClass ? klass.getMethods() |
1703 | : klass.getDeclaredMethods(); | |
1704 | 283 | for (final Method method : methods) |
1705 | { | |
1706 | 5989 | final int modifiers = method.getModifiers(); |
1707 | 5989 | if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) |
1708 | && method.getParameterTypes().length == 0 | |
1709 | && !method.isBridge() && method.getReturnType() != Void.TYPE | |
1710 | && isValidMethodName(method.getName())) | |
1711 | { | |
1712 | 2287 | final String key = getKeyNameFromMethod(method); |
1713 | 2287 | if (key != null && !key.isEmpty()) |
1714 | { | |
1715 | 1721 | try |
1716 | { | |
1717 | 1721 | final Object result = method.invoke(bean); |
1718 | 1721 | if (result != null) |
1719 | { | |
1720 | 1244 | this.map.put(key, wrap(result)); |
1721 | // we don't use the result anywhere outside of wrap | |
1722 | // if it's a resource we should be sure to close it | |
1723 | // after calling toString | |
1724 | 1244 | if (result instanceof Closeable) |
1725 | { | |
1726 | 0 | try |
1727 | { | |
1728 | 0 | ((Closeable) result).close(); |
1729 | } catch (IOException ignore) | |
1730 | { | |
1731 | } | |
1732 | } | |
1733 | } | |
1734 | } catch (IllegalAccessException ignore) | |
1735 | { | |
1736 | } catch (IllegalArgumentException ignore) | |
1737 | { | |
1738 | } catch (InvocationTargetException ignore) | |
1739 | { | |
1740 | } | |
1741 | } | |
1742 | } | |
1743 | } | |
1744 | } | |
1745 | ||
1746 | 2570 | private boolean isValidMethodName(String name) |
1747 | { | |
1748 | 2570 | return !"getClass".equals(name) && !"getDeclaringClass".equals(name); |
1749 | } | |
1750 | ||
1751 | 2287 | private String getKeyNameFromMethod(Method method) |
1752 | { | |
1753 | 2287 | final int ignoreDepth = -1;// getAnnotationDepth(method, |
1754 | // JSONPropertyIgnore.class); | |
1755 | // if (ignoreDepth > 0) { | |
1756 | // final int forcedNameDepth = getAnnotationDepth(method, | |
1757 | // JSONPropertyName.class); | |
1758 | // if (forcedNameDepth < 0 || ignoreDepth <= forcedNameDepth) { | |
1759 | // // the hierarchy asked to ignore, and the nearest name override | |
1760 | // // was higher or non-existent | |
1761 | // return null; | |
1762 | // } | |
1763 | // } | |
1764 | // JSONPropertyName annotation = getAnnotation(method, | |
1765 | // JSONPropertyName.class); | |
1766 | // if (annotation != null && annotation.value() != null && | |
1767 | // !annotation.value().isEmpty()) { | |
1768 | // return annotation.value(); | |
1769 | // } | |
1770 | 2287 | String key; |
1771 | 2287 | final String name = method.getName(); |
1772 | 2287 | if (name.startsWith("get") && name.length() > 3) |
1773 | { | |
1774 | 1667 | key = name.substring(3); |
1775 | } | |
1776 | 620 | else if (name.startsWith("is") && name.length() > 2) |
1777 | { | |
1778 | 54 | key = name.substring(2); |
1779 | } | |
1780 | else | |
1781 | { | |
1782 | 566 | return null; |
1783 | } | |
1784 | // if the first letter in the key is not uppercase, then skip. | |
1785 | // This is to maintain backwards compatibility before PR406 | |
1786 | // (https://github.com/stleary/JSON-java/pull/406/) | |
1787 | 1721 | if (Character.isLowerCase(key.charAt(0))) |
1788 | { | |
1789 | 0 | return null; |
1790 | } | |
1791 | 1721 | if (key.length() == 1) |
1792 | { | |
1793 | 0 | key = key.toLowerCase(Locale.ROOT); |
1794 | } | |
1795 | 1721 | else if (!Character.isUpperCase(key.charAt(1))) |
1796 | { | |
1797 | 1721 | key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1); |
1798 | } | |
1799 | 1721 | return (/** @j2sNative 1 ? key.split("$")[0] : */ |
1800 | key); | |
1801 | } | |
1802 | ||
1803 | // /** | |
1804 | // * Searches the class hierarchy to see if the method or it's super | |
1805 | // * implementations and interfaces has the annotation. | |
1806 | // * | |
1807 | // * @param <A> | |
1808 | // * type of the annotation | |
1809 | // * | |
1810 | // * @param m | |
1811 | // * method to check | |
1812 | // * @param annotationClass | |
1813 | // * annotation to look for | |
1814 | // * @return the {@link Annotation} if the annotation exists on the current | |
1815 | // method | |
1816 | // * or one of it's super class definitions | |
1817 | // */ | |
1818 | // private static <A extends Annotation> A getAnnotation(final Method m, final | |
1819 | // Class<A> annotationClass) { | |
1820 | // return null; | |
1821 | // // if we have invalid data the result is null | |
1822 | // if (true || m == null || annotationClass == null) { | |
1823 | // return null; | |
1824 | // } | |
1825 | // | |
1826 | // if (m.isAnnotationPresent(annotationClass)) { | |
1827 | // return m.getAnnotation(annotationClass); | |
1828 | // } | |
1829 | // | |
1830 | // // if we've already reached the Object class, return null; | |
1831 | // Class<?> c = m.getDeclaringClass(); | |
1832 | // if (c.getSuperclass() == null) { | |
1833 | // return null; | |
1834 | // } | |
1835 | // | |
1836 | // // check directly implemented interfaces for the method being checked | |
1837 | // for (Class<?> i : c.getInterfaces()) { | |
1838 | // try { | |
1839 | // Method im = i.getMethod(m.getName(), m.getParameterTypes()); | |
1840 | // return getAnnotation(im, annotationClass); | |
1841 | // } catch (final SecurityException ex) { | |
1842 | // continue; | |
1843 | // } catch (final NoSuchMethodException ex) { | |
1844 | // continue; | |
1845 | // } | |
1846 | // } | |
1847 | // | |
1848 | // try { | |
1849 | // return getAnnotation( | |
1850 | // c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), | |
1851 | // annotationClass); | |
1852 | // } catch (final SecurityException ex) { | |
1853 | // return null; | |
1854 | // } catch (final NoSuchMethodException ex) { | |
1855 | // return null; | |
1856 | // } | |
1857 | // } | |
1858 | // | |
1859 | // /** | |
1860 | // * Searches the class hierarchy to see if the method or it's super | |
1861 | // * implementations and interfaces has the annotation. Returns the depth of | |
1862 | // the | |
1863 | // * annotation in the hierarchy. | |
1864 | // * | |
1865 | // * @param <A> | |
1866 | // * type of the annotation | |
1867 | // * | |
1868 | // * @param m | |
1869 | // * method to check | |
1870 | // * @param annotationClass | |
1871 | // * annotation to look for | |
1872 | // * @return Depth of the annotation or -1 if the annotation is not on the | |
1873 | // method. | |
1874 | // */ | |
1875 | // private static int getAnnotationDepth(final Method m, final Class<? extends | |
1876 | // Annotation> annotationClass) { | |
1877 | // // if we have invalid data the result is -1 | |
1878 | // if (m == null || annotationClass == null) { | |
1879 | // return -1; | |
1880 | // } | |
1881 | // if (m.isAnnotationPresent(annotationClass)) { | |
1882 | // return 1; | |
1883 | // } | |
1884 | // | |
1885 | // // if we've already reached the Object class, return -1; | |
1886 | // Class<?> c = m.getDeclaringClass(); | |
1887 | // if (c.getSuperclass() == null) { | |
1888 | // return -1; | |
1889 | // } | |
1890 | // | |
1891 | // // check directly implemented interfaces for the method being checked | |
1892 | // for (Class<?> i : c.getInterfaces()) { | |
1893 | // try { | |
1894 | // Method im = i.getMethod(m.getName(), m.getParameterTypes()); | |
1895 | // int d = getAnnotationDepth(im, annotationClass); | |
1896 | // if (d > 0) { | |
1897 | // // since the annotation was on the interface, add 1 | |
1898 | // return d + 1; | |
1899 | // } | |
1900 | // } catch (final SecurityException ex) { | |
1901 | // continue; | |
1902 | // } catch (final NoSuchMethodException ex) { | |
1903 | // continue; | |
1904 | // } | |
1905 | // } | |
1906 | // | |
1907 | // try { | |
1908 | // int d = getAnnotationDepth( | |
1909 | // c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), | |
1910 | // annotationClass); | |
1911 | // if (d > 0) { | |
1912 | // // since the annotation was on the superclass, add 1 | |
1913 | // return d + 1; | |
1914 | // } | |
1915 | // return -1; | |
1916 | // } catch (final SecurityException ex) { | |
1917 | // return -1; | |
1918 | // } catch (final NoSuchMethodException ex) { | |
1919 | // return -1; | |
1920 | // } | |
1921 | // } | |
1922 | ||
1923 | /** | |
1924 | * Put a key/boolean pair in the JSONObject. | |
1925 | * | |
1926 | * @param key | |
1927 | * A key string. | |
1928 | * @param value | |
1929 | * A boolean which is the value. | |
1930 | * @return this. | |
1931 | * @throws JSONException | |
1932 | * If the value is non-finite number. | |
1933 | * @throws NullPointerException | |
1934 | * If the key is <code>null</code>. | |
1935 | */ | |
1936 | 0 | public JSONObject put(String key, boolean value) throws JSONException |
1937 | { | |
1938 | 0 | return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); |
1939 | } | |
1940 | ||
1941 | /** | |
1942 | * Put a key/value pair in the JSONObject, where the value will be a JSONArray | |
1943 | * which is produced from a Collection. | |
1944 | * | |
1945 | * @param key | |
1946 | * A key string. | |
1947 | * @param value | |
1948 | * A Collection value. | |
1949 | * @return this. | |
1950 | * @throws JSONException | |
1951 | * If the value is non-finite number. | |
1952 | * @throws NullPointerException | |
1953 | * If the key is <code>null</code>. | |
1954 | */ | |
1955 | 0 | public JSONObject put(String key, Collection<?> value) |
1956 | throws JSONException | |
1957 | { | |
1958 | 0 | return this.put(key, new JSONArray(value)); |
1959 | } | |
1960 | ||
1961 | /** | |
1962 | * Put a key/double pair in the JSONObject. | |
1963 | * | |
1964 | * @param key | |
1965 | * A key string. | |
1966 | * @param value | |
1967 | * A double which is the value. | |
1968 | * @return this. | |
1969 | * @throws JSONException | |
1970 | * If the value is non-finite number. | |
1971 | * @throws NullPointerException | |
1972 | * If the key is <code>null</code>. | |
1973 | */ | |
1974 | 0 | public JSONObject put(String key, double value) throws JSONException |
1975 | { | |
1976 | 0 | return this.put(key, Double.valueOf(value)); |
1977 | } | |
1978 | ||
1979 | /** | |
1980 | * Put a key/float pair in the JSONObject. | |
1981 | * | |
1982 | * @param key | |
1983 | * A key string. | |
1984 | * @param value | |
1985 | * A float which is the value. | |
1986 | * @return this. | |
1987 | * @throws JSONException | |
1988 | * If the value is non-finite number. | |
1989 | * @throws NullPointerException | |
1990 | * If the key is <code>null</code>. | |
1991 | */ | |
1992 | 0 | public JSONObject put(String key, float value) throws JSONException |
1993 | { | |
1994 | 0 | return this.put(key, Float.valueOf(value)); |
1995 | } | |
1996 | ||
1997 | /** | |
1998 | * Put a key/int pair in the JSONObject. | |
1999 | * | |
2000 | * @param key | |
2001 | * A key string. | |
2002 | * @param value | |
2003 | * An int which is the value. | |
2004 | * @return this. | |
2005 | * @throws JSONException | |
2006 | * If the value is non-finite number. | |
2007 | * @throws NullPointerException | |
2008 | * If the key is <code>null</code>. | |
2009 | */ | |
2010 | 0 | public JSONObject put(String key, int value) throws JSONException |
2011 | { | |
2012 | 0 | return this.put(key, Integer.valueOf(value)); |
2013 | } | |
2014 | ||
2015 | /** | |
2016 | * Put a key/long pair in the JSONObject. | |
2017 | * | |
2018 | * @param key | |
2019 | * A key string. | |
2020 | * @param value | |
2021 | * A long which is the value. | |
2022 | * @return this. | |
2023 | * @throws JSONException | |
2024 | * If the value is non-finite number. | |
2025 | * @throws NullPointerException | |
2026 | * If the key is <code>null</code>. | |
2027 | */ | |
2028 | 0 | public JSONObject put(String key, long value) throws JSONException |
2029 | { | |
2030 | 0 | return this.put(key, Long.valueOf(value)); |
2031 | } | |
2032 | ||
2033 | /** | |
2034 | * Put a key/value pair in the JSONObject, where the value will be a | |
2035 | * JSONObject which is produced from a Map. | |
2036 | * | |
2037 | * @param key | |
2038 | * A key string. | |
2039 | * @param value | |
2040 | * A Map value. | |
2041 | * @return this. | |
2042 | * @throws JSONException | |
2043 | * If the value is non-finite number. | |
2044 | * @throws NullPointerException | |
2045 | * If the key is <code>null</code>. | |
2046 | */ | |
2047 | 0 | public JSONObject put(String key, Map<?, ?> value) throws JSONException |
2048 | { | |
2049 | 0 | return this.put(key, new JSONObject(value)); |
2050 | } | |
2051 | ||
2052 | /** | |
2053 | * Put a key/value pair in the JSONObject. If the value is <code>null</code>, | |
2054 | * then the key will be removed from the JSONObject if it is present. | |
2055 | * | |
2056 | * @param key | |
2057 | * A key string. | |
2058 | * @param value | |
2059 | * An object which is the value. It should be of one of these types: | |
2060 | * Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or | |
2061 | * the JSONObject.NULL object. | |
2062 | * @return this. | |
2063 | * @throws JSONException | |
2064 | * If the value is non-finite number. | |
2065 | * @throws NullPointerException | |
2066 | * If the key is <code>null</code>. | |
2067 | */ | |
2068 | 0 | public JSONObject put(String key, Object value) throws JSONException |
2069 | { | |
2070 | 0 | if (key == null) |
2071 | { | |
2072 | 0 | throw new NullPointerException("Null key."); |
2073 | } | |
2074 | 0 | if (value != null) |
2075 | { | |
2076 | 0 | testValidity(value); |
2077 | 0 | this.map.put(key, value); |
2078 | } | |
2079 | else | |
2080 | { | |
2081 | 0 | this.remove(key); |
2082 | } | |
2083 | 0 | return this; |
2084 | } | |
2085 | ||
2086 | /** | |
2087 | * Put a key/value pair in the JSONObject, but only if the key and the value | |
2088 | * are both non-null, and only if there is not already a member with that | |
2089 | * name. | |
2090 | * | |
2091 | * @param key | |
2092 | * string | |
2093 | * @param value | |
2094 | * object | |
2095 | * @return this. | |
2096 | * @throws JSONException | |
2097 | * if the key is a duplicate | |
2098 | */ | |
2099 | 0 | public JSONObject putOnce(String key, Object value) throws JSONException |
2100 | { | |
2101 | 0 | if (key != null && value != null) |
2102 | { | |
2103 | 0 | if (this.opt(key) != null) |
2104 | { | |
2105 | 0 | throw new JSONException("Duplicate key \"" + key + "\""); |
2106 | } | |
2107 | 0 | return this.put(key, value); |
2108 | } | |
2109 | 0 | return this; |
2110 | } | |
2111 | ||
2112 | /** | |
2113 | * Put a key/value pair in the JSONObject, but only if the key and the value | |
2114 | * are both non-null. | |
2115 | * | |
2116 | * @param key | |
2117 | * A key string. | |
2118 | * @param value | |
2119 | * An object which is the value. It should be of one of these types: | |
2120 | * Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or | |
2121 | * the JSONObject.NULL object. | |
2122 | * @return this. | |
2123 | * @throws JSONException | |
2124 | * If the value is a non-finite number. | |
2125 | */ | |
2126 | 0 | public JSONObject putOpt(String key, Object value) throws JSONException |
2127 | { | |
2128 | 0 | if (key != null && value != null) |
2129 | { | |
2130 | 0 | return this.put(key, value); |
2131 | } | |
2132 | 0 | return this; |
2133 | } | |
2134 | ||
2135 | /** | |
2136 | * Creates a JSONPointer using an initialization string and tries to match it | |
2137 | * to an item within this JSONObject. For example, given a JSONObject | |
2138 | * initialized with this document: | |
2139 | * | |
2140 | * <pre> | |
2141 | * { | |
2142 | * "a":{"b":"c"} | |
2143 | * } | |
2144 | * </pre> | |
2145 | * | |
2146 | * and this JSONPointer string: | |
2147 | * | |
2148 | * <pre> | |
2149 | * "/a/b" | |
2150 | * </pre> | |
2151 | * | |
2152 | * Then this method will return the String "c". A JSONPointerException may be | |
2153 | * thrown from code called by this method. | |
2154 | * | |
2155 | * @param jsonPointer | |
2156 | * string that can be used to create a JSONPointer | |
2157 | * @return the item matched by the JSONPointer, otherwise null | |
2158 | */ | |
2159 | 0 | public Object query(String jsonPointer) |
2160 | { | |
2161 | 0 | return query(new JSONPointer(jsonPointer)); |
2162 | } | |
2163 | ||
2164 | /** | |
2165 | * Uses a user initialized JSONPointer and tries to match it to an item within | |
2166 | * this JSONObject. For example, given a JSONObject initialized with this | |
2167 | * document: | |
2168 | * | |
2169 | * <pre> | |
2170 | * { | |
2171 | * "a":{"b":"c"} | |
2172 | * } | |
2173 | * </pre> | |
2174 | * | |
2175 | * and this JSONPointer: | |
2176 | * | |
2177 | * <pre> | |
2178 | * "/a/b" | |
2179 | * </pre> | |
2180 | * | |
2181 | * Then this method will return the String "c". A JSONPointerException may be | |
2182 | * thrown from code called by this method. | |
2183 | * | |
2184 | * @param jsonPointer | |
2185 | * string that can be used to create a JSONPointer | |
2186 | * @return the item matched by the JSONPointer, otherwise null | |
2187 | */ | |
2188 | 0 | public Object query(JSONPointer jsonPointer) |
2189 | { | |
2190 | 0 | return jsonPointer.queryFrom(this); |
2191 | } | |
2192 | ||
2193 | /** | |
2194 | * Queries and returns a value from this object using {@code jsonPointer}, or | |
2195 | * returns null if the query fails due to a missing key. | |
2196 | * | |
2197 | * @param jsonPointer | |
2198 | * the string representation of the JSON pointer | |
2199 | * @return the queried value or {@code null} | |
2200 | * @throws IllegalArgumentException | |
2201 | * if {@code jsonPointer} has invalid syntax | |
2202 | */ | |
2203 | 0 | public Object optQuery(String jsonPointer) |
2204 | { | |
2205 | 0 | return optQuery(new JSONPointer(jsonPointer)); |
2206 | } | |
2207 | ||
2208 | /** | |
2209 | * Queries and returns a value from this object using {@code jsonPointer}, or | |
2210 | * returns null if the query fails due to a missing key. | |
2211 | * | |
2212 | * @param jsonPointer | |
2213 | * The JSON pointer | |
2214 | * @return the queried value or {@code null} | |
2215 | * @throws IllegalArgumentException | |
2216 | * if {@code jsonPointer} has invalid syntax | |
2217 | */ | |
2218 | 0 | public Object optQuery(JSONPointer jsonPointer) |
2219 | { | |
2220 | 0 | try |
2221 | { | |
2222 | 0 | return jsonPointer.queryFrom(this); |
2223 | } catch (JSONPointerException e) | |
2224 | { | |
2225 | 0 | return null; |
2226 | } | |
2227 | } | |
2228 | ||
2229 | /** | |
2230 | * Produce a string in double quotes with backslash sequences in all the right | |
2231 | * places. A backslash will be inserted within </, producing <\/, allowing | |
2232 | * JSON text to be delivered in HTML. In JSON text, a string cannot contain a | |
2233 | * control character or an unescaped quote or backslash. | |
2234 | * | |
2235 | * @param string | |
2236 | * A String | |
2237 | * @return A String correctly formatted for insertion in a JSON text. | |
2238 | */ | |
2239 | 1286 | public static String quote(String string) |
2240 | { | |
2241 | 1286 | StringWriter sw = new StringWriter(); |
2242 | 1286 | synchronized (sw.getBuffer()) |
2243 | { | |
2244 | 1286 | try |
2245 | { | |
2246 | 1286 | return quote(string, sw).toString(); |
2247 | } catch (IOException ignored) | |
2248 | { | |
2249 | // will never happen - we are writing to a string writer | |
2250 | 0 | return ""; |
2251 | } | |
2252 | } | |
2253 | } | |
2254 | ||
2255 | 2037 | public static Writer quote(String string, Writer w) throws IOException |
2256 | { | |
2257 | 2037 | if (string == null || string.isEmpty()) |
2258 | { | |
2259 | 100 | w.write("\"\""); |
2260 | 100 | return w; |
2261 | } | |
2262 | ||
2263 | 1937 | char b; |
2264 | 1937 | char c = 0; |
2265 | 1937 | String hhhh; |
2266 | 1937 | int i; |
2267 | 1937 | int len = string.length(); |
2268 | ||
2269 | 1937 | w.write('"'); |
2270 | 23361 | for (i = 0; i < len; i += 1) |
2271 | { | |
2272 | 21424 | b = c; |
2273 | 21424 | c = string.charAt(i); |
2274 | 21424 | switch (c) |
2275 | { | |
2276 | 0 | case '\\': |
2277 | 0 | case '"': |
2278 | 0 | w.write('\\'); |
2279 | 0 | w.write(c); |
2280 | 0 | break; |
2281 | 92 | case '/': |
2282 | 92 | if (b == '<') |
2283 | { | |
2284 | 0 | w.write('\\'); |
2285 | } | |
2286 | 92 | w.write(c); |
2287 | 92 | break; |
2288 | 0 | case '\b': |
2289 | 0 | w.write("\\b"); |
2290 | 0 | break; |
2291 | 0 | case '\t': |
2292 | 0 | w.write("\\t"); |
2293 | 0 | break; |
2294 | 0 | case '\n': |
2295 | 0 | w.write("\\n"); |
2296 | 0 | break; |
2297 | 0 | case '\f': |
2298 | 0 | w.write("\\f"); |
2299 | 0 | break; |
2300 | 0 | case '\r': |
2301 | 0 | w.write("\\r"); |
2302 | 0 | break; |
2303 | 21332 | default: |
2304 | 21332 | if (c < ' ' || (c >= '\u0080' && c < '\u00a0') |
2305 | || (c >= '\u2000' && c < '\u2100')) | |
2306 | { | |
2307 | 95 | w.write("\\u"); |
2308 | 95 | hhhh = Integer.toHexString(c); |
2309 | 95 | w.write("0000", 0, 4 - hhhh.length()); |
2310 | 95 | w.write(hhhh); |
2311 | } | |
2312 | else | |
2313 | { | |
2314 | 21237 | w.write(c); |
2315 | } | |
2316 | } | |
2317 | } | |
2318 | 1937 | w.write('"'); |
2319 | 1937 | return w; |
2320 | } | |
2321 | ||
2322 | /** | |
2323 | * Remove a name and its value, if present. | |
2324 | * | |
2325 | * @param key | |
2326 | * The name to be removed. | |
2327 | * @return The value that was associated with the name, or null if there was | |
2328 | * no value. | |
2329 | */ | |
2330 | 0 | public Object remove(String key) |
2331 | { | |
2332 | 0 | return this.map.remove(key); |
2333 | } | |
2334 | ||
2335 | /** | |
2336 | * Determine if two JSONObjects are similar. They must contain the same set of | |
2337 | * names which must be associated with similar values. | |
2338 | * | |
2339 | * @param other | |
2340 | * The other JSONObject | |
2341 | * @return true if they are equal | |
2342 | */ | |
2343 | 0 | public boolean similar(Object other) |
2344 | { | |
2345 | 0 | try |
2346 | { | |
2347 | 0 | if (!(other instanceof JSONObject)) |
2348 | { | |
2349 | 0 | return false; |
2350 | } | |
2351 | 0 | if (!this.keySet().equals(((JSONObject) other).keySet())) |
2352 | { | |
2353 | 0 | return false; |
2354 | } | |
2355 | 0 | for (final Entry<String, ?> entry : this.entrySet()) |
2356 | { | |
2357 | 0 | String name = entry.getKey(); |
2358 | 0 | Object valueThis = entry.getValue(); |
2359 | 0 | Object valueOther = ((JSONObject) other).get(name); |
2360 | 0 | if (valueThis == valueOther) |
2361 | { | |
2362 | 0 | continue; |
2363 | } | |
2364 | 0 | if (valueThis == null) |
2365 | { | |
2366 | 0 | return false; |
2367 | } | |
2368 | 0 | if (valueThis instanceof JSONObject) |
2369 | { | |
2370 | 0 | if (!((JSONObject) valueThis).similar(valueOther)) |
2371 | { | |
2372 | 0 | return false; |
2373 | } | |
2374 | } | |
2375 | 0 | else if (valueThis instanceof JSONArray) |
2376 | { | |
2377 | 0 | if (!((JSONArray) valueThis).similar(valueOther)) |
2378 | { | |
2379 | 0 | return false; |
2380 | } | |
2381 | } | |
2382 | 0 | else if (!valueThis.equals(valueOther)) |
2383 | { | |
2384 | 0 | return false; |
2385 | } | |
2386 | } | |
2387 | 0 | return true; |
2388 | } catch (Throwable exception) | |
2389 | { | |
2390 | 0 | return false; |
2391 | } | |
2392 | } | |
2393 | ||
2394 | /** | |
2395 | * Tests if the value should be tried as a decimal. It makes no test if there | |
2396 | * are actual digits. | |
2397 | * | |
2398 | * @param val | |
2399 | * value to test | |
2400 | * @return true if the string is "-0" or if it contains '.', 'e', or 'E', | |
2401 | * false otherwise. | |
2402 | */ | |
2403 | 0 | protected static boolean isDecimalNotation(final String val) |
2404 | { | |
2405 | 0 | return val.indexOf('.') > -1 || val.indexOf('e') > -1 |
2406 | || val.indexOf('E') > -1 || "-0".equals(val); | |
2407 | } | |
2408 | ||
2409 | /** | |
2410 | * Converts a string to a number using the narrowest possible type. Possible | |
2411 | * returns for this function are BigDecimal, Double, BigInteger, Long, and | |
2412 | * Integer. When a Double is returned, it should always be a valid Double and | |
2413 | * not NaN or +-infinity. | |
2414 | * | |
2415 | * @param val | |
2416 | * value to convert | |
2417 | * @return Number representation of the value. | |
2418 | * @throws NumberFormatException | |
2419 | * thrown if the value is not a valid number. A public caller should | |
2420 | * catch this and wrap it in a {@link JSONException} if applicable. | |
2421 | */ | |
2422 | 0 | protected static Number stringToNumber(final String val) |
2423 | throws NumberFormatException | |
2424 | { | |
2425 | 0 | char initial = val.charAt(0); |
2426 | 0 | if ((initial >= '0' && initial <= '9') || initial == '-') |
2427 | { | |
2428 | // decimal representation | |
2429 | 0 | if (isDecimalNotation(val)) |
2430 | { | |
2431 | // quick dirty way to see if we need a BigDecimal instead of a Double | |
2432 | // this only handles some cases of overflow or underflow | |
2433 | 0 | if (val.length() > 14) |
2434 | { | |
2435 | 0 | return new BigDecimal(val); |
2436 | } | |
2437 | 0 | final Double d = Double.valueOf(val); |
2438 | 0 | if (d.isInfinite() || d.isNaN()) |
2439 | { | |
2440 | // if we can't parse it as a double, go up to BigDecimal | |
2441 | // this is probably due to underflow like 4.32e-678 | |
2442 | // or overflow like 4.65e5324. The size of the string is small | |
2443 | // but can't be held in a Double. | |
2444 | 0 | return new BigDecimal(val); |
2445 | } | |
2446 | 0 | return d; |
2447 | } | |
2448 | // integer representation. | |
2449 | // This will narrow any values to the smallest reasonable Object | |
2450 | // representation | |
2451 | // (Integer, Long, or BigInteger) | |
2452 | ||
2453 | // string version | |
2454 | // The compare string length method reduces GC, | |
2455 | // but leads to smaller integers being placed in larger wrappers even | |
2456 | // though not | |
2457 | // needed. i.e. 1,000,000,000 -> Long even though it's an Integer | |
2458 | // 1,000,000,000,000,000,000 -> BigInteger even though it's a Long | |
2459 | // if(val.length()<=9){ | |
2460 | // return Integer.valueOf(val); | |
2461 | // } | |
2462 | // if(val.length()<=18){ | |
2463 | // return Long.valueOf(val); | |
2464 | // } | |
2465 | // return new BigInteger(val); | |
2466 | ||
2467 | // BigInteger version: We use a similar bitLenth compare as | |
2468 | // BigInteger#intValueExact uses. Increases GC, but objects hold | |
2469 | // only what they need. i.e. Less runtime overhead if the value is | |
2470 | // long lived. Which is the better tradeoff? This is closer to what's | |
2471 | // in stringToValue. | |
2472 | 0 | BigInteger bi = new BigInteger(val); |
2473 | 0 | if (bi.bitLength() <= 31) |
2474 | { | |
2475 | 0 | return Integer.valueOf(bi.intValue()); |
2476 | } | |
2477 | 0 | if (bi.bitLength() <= 63) |
2478 | { | |
2479 | 0 | return Long.valueOf(bi.longValue()); |
2480 | } | |
2481 | 0 | return bi; |
2482 | } | |
2483 | 0 | throw new NumberFormatException( |
2484 | "val [" + val + "] is not a valid number."); | |
2485 | } | |
2486 | ||
2487 | /** | |
2488 | * Try to convert a string into a number, boolean, or null. If the string | |
2489 | * can't be converted, return the string. | |
2490 | * | |
2491 | * @param string | |
2492 | * A String. | |
2493 | * @return A simple JSON value. | |
2494 | */ | |
2495 | // Changes to this method must be copied to the corresponding method in | |
2496 | // the XML class to keep full support for Android | |
2497 | 0 | public static Object stringToValue(String string) |
2498 | { | |
2499 | 0 | if (string.equals("")) |
2500 | { | |
2501 | 0 | return string; |
2502 | } | |
2503 | 0 | if (string.equalsIgnoreCase("true")) |
2504 | { | |
2505 | 0 | return Boolean.TRUE; |
2506 | } | |
2507 | 0 | if (string.equalsIgnoreCase("false")) |
2508 | { | |
2509 | 0 | return Boolean.FALSE; |
2510 | } | |
2511 | 0 | if (string.equalsIgnoreCase("null")) |
2512 | { | |
2513 | 0 | return JSONObject.NULL; |
2514 | } | |
2515 | ||
2516 | /* | |
2517 | * If it might be a number, try converting it. If a number cannot be produced, | |
2518 | * then the value will just be a string. | |
2519 | */ | |
2520 | ||
2521 | 0 | char initial = string.charAt(0); |
2522 | 0 | if ((initial >= '0' && initial <= '9') || initial == '-') |
2523 | { | |
2524 | 0 | try |
2525 | { | |
2526 | // if we want full Big Number support this block can be replaced with: | |
2527 | // return stringToNumber(string); | |
2528 | 0 | if (isDecimalNotation(string)) |
2529 | { | |
2530 | 0 | Double d = Double.valueOf(string); |
2531 | 0 | if (!d.isInfinite() && !d.isNaN()) |
2532 | { | |
2533 | 0 | return d; |
2534 | } | |
2535 | } | |
2536 | else | |
2537 | { | |
2538 | 0 | Long myLong = Long.valueOf(string); |
2539 | 0 | if (string.equals(myLong.toString())) |
2540 | { | |
2541 | 0 | if (myLong.longValue() == myLong.intValue()) |
2542 | { | |
2543 | 0 | return Integer.valueOf(myLong.intValue()); |
2544 | } | |
2545 | 0 | return myLong; |
2546 | } | |
2547 | } | |
2548 | } catch (Exception ignore) | |
2549 | { | |
2550 | } | |
2551 | } | |
2552 | 0 | return string; |
2553 | } | |
2554 | ||
2555 | /** | |
2556 | * Throw an exception if the object is a NaN or infinite number. | |
2557 | * | |
2558 | * @param o | |
2559 | * The object to test. | |
2560 | * @throws JSONException | |
2561 | * If o is a non-finite number. | |
2562 | */ | |
2563 | 449 | public static void testValidity(Object o) throws JSONException |
2564 | { | |
2565 | 449 | if (o != null) |
2566 | { | |
2567 | 449 | if (o instanceof Double) |
2568 | { | |
2569 | 5 | if (((Double) o).isInfinite() || ((Double) o).isNaN()) |
2570 | { | |
2571 | 0 | throw new JSONException( |
2572 | "JSON does not allow non-finite numbers."); | |
2573 | } | |
2574 | } | |
2575 | 444 | else if (o instanceof Float) |
2576 | { | |
2577 | 199 | if (((Float) o).isInfinite() || ((Float) o).isNaN()) |
2578 | { | |
2579 | 0 | throw new JSONException( |
2580 | "JSON does not allow non-finite numbers."); | |
2581 | } | |
2582 | } | |
2583 | } | |
2584 | } | |
2585 | ||
2586 | /** | |
2587 | * Produce a JSONArray containing the values of the members of this | |
2588 | * JSONObject. | |
2589 | * | |
2590 | * @param names | |
2591 | * A JSONArray containing a list of key strings. This determines the | |
2592 | * sequence of the values in the result. | |
2593 | * @return A JSONArray of values. | |
2594 | * @throws JSONException | |
2595 | * If any of the values are non-finite numbers. | |
2596 | */ | |
2597 | 0 | public JSONArray toJSONArray(JSONArray names) throws JSONException |
2598 | { | |
2599 | 0 | if (names == null || names.isEmpty()) |
2600 | { | |
2601 | 0 | return null; |
2602 | } | |
2603 | 0 | JSONArray ja = new JSONArray(); |
2604 | 0 | for (int i = 0; i < names.length(); i += 1) |
2605 | { | |
2606 | 0 | ja.put(this.opt(names.getString(i))); |
2607 | } | |
2608 | 0 | return ja; |
2609 | } | |
2610 | ||
2611 | /** | |
2612 | * Make a JSON text of this JSONObject. For compactness, no whitespace is | |
2613 | * added. If this would not result in a syntactically correct JSON text, then | |
2614 | * null will be returned instead. | |
2615 | * <p> | |
2616 | * <b> Warning: This method assumes that the data structure is acyclical. </b> | |
2617 | * | |
2618 | * @return a printable, displayable, portable, transmittable representation of | |
2619 | * the object, beginning with <code>{</code> <small>(left | |
2620 | * brace)</small> and ending with <code>}</code> <small>(right | |
2621 | * brace)</small>. | |
2622 | */ | |
2623 | 8 | @Override |
2624 | public String toString() | |
2625 | { | |
2626 | 8 | try |
2627 | { | |
2628 | 8 | return this.toString(0); |
2629 | } catch (Exception e) | |
2630 | { | |
2631 | 0 | return null; |
2632 | } | |
2633 | } | |
2634 | ||
2635 | /** | |
2636 | * Make a pretty-printed JSON text of this JSONObject. | |
2637 | * | |
2638 | * <p> | |
2639 | * If <code>indentFactor > 0</code> and the {@link JSONObject} has only one | |
2640 | * key, then the object will be output on a single line: | |
2641 | * | |
2642 | * <pre> | |
2643 | * {@code {"key": 1}} | |
2644 | * </pre> | |
2645 | * | |
2646 | * <p> | |
2647 | * If an object has 2 or more keys, then it will be output across multiple | |
2648 | * lines: <code><pre>{ | |
2649 | * "key1": 1, | |
2650 | * "key2": "value 2", | |
2651 | * "key3": 3 | |
2652 | * }</pre></code> | |
2653 | * <p> | |
2654 | * <b> Warning: This method assumes that the data structure is acyclical. </b> | |
2655 | * | |
2656 | * @param indentFactor | |
2657 | * The number of spaces to add to each level of indentation. | |
2658 | * @return a printable, displayable, portable, transmittable representation of | |
2659 | * the object, beginning with <code>{</code> <small>(left | |
2660 | * brace)</small> and ending with <code>}</code> <small>(right | |
2661 | * brace)</small>. | |
2662 | * @throws JSONException | |
2663 | * If the object contains an invalid number. | |
2664 | */ | |
2665 | 8 | public String toString(int indentFactor) throws JSONException |
2666 | { | |
2667 | 8 | StringWriter w = new StringWriter(); |
2668 | 8 | synchronized (w.getBuffer()) |
2669 | { | |
2670 | 8 | return this.write(w, indentFactor, 0).toString(); |
2671 | } | |
2672 | } | |
2673 | ||
2674 | /** | |
2675 | * Make a JSON text of an Object value. If the object has an | |
2676 | * value.toJSONString() method, then that method will be used to produce the | |
2677 | * JSON text. The method is required to produce a strictly conforming text. If | |
2678 | * the object does not contain a toJSONString method (which is the most common | |
2679 | * case), then a text will be produced by other means. If the value is an | |
2680 | * array or Collection, then a JSONArray will be made from it and its | |
2681 | * toJSONString method will be called. If the value is a MAP, then a | |
2682 | * JSONObject will be made from it and its toJSONString method will be called. | |
2683 | * Otherwise, the value's toString method will be called, and the result will | |
2684 | * be quoted. | |
2685 | * | |
2686 | * <p> | |
2687 | * Warning: This method assumes that the data structure is acyclical. | |
2688 | * | |
2689 | * @param value | |
2690 | * The value to be serialized. | |
2691 | * @return a printable, displayable, transmittable representation of the | |
2692 | * object, beginning with <code>{</code> <small>(left | |
2693 | * brace)</small> and ending with <code>}</code> <small>(right | |
2694 | * brace)</small>. | |
2695 | * @throws JSONException | |
2696 | * If the value is or contains an invalid number. | |
2697 | */ | |
2698 | 0 | public static String valueToString(Object value) throws JSONException |
2699 | { | |
2700 | // moves the implementation to JSONWriter as: | |
2701 | // 1. It makes more sense to be part of the writer class | |
2702 | // 2. For Android support this method is not available. By implementing it | |
2703 | // in | |
2704 | // the Writer | |
2705 | // Android users can use the writer with the built in Android JSONObject | |
2706 | // implementation. | |
2707 | 0 | return JSONWriter.valueToString(value); |
2708 | } | |
2709 | ||
2710 | /** | |
2711 | * Wrap an object, if necessary. If the object is <code>null</code>, return | |
2712 | * the NULL object. If it is an array or collection, wrap it in a JSONArray. | |
2713 | * If it is a map, wrap it in a JSONObject. If it is a standard property | |
2714 | * (Double, String, et al) then it is already wrapped. Otherwise, if it comes | |
2715 | * from one of the java packages, turn it into a string. And if it doesn't, | |
2716 | * try to wrap it in a JSONObject. If the wrapping fails, then null is | |
2717 | * returned. | |
2718 | * | |
2719 | * @param object | |
2720 | * The object to wrap | |
2721 | * @return The wrapped value | |
2722 | */ | |
2723 | 1580 | public static Object wrap(Object object) |
2724 | { | |
2725 | 1580 | try |
2726 | { | |
2727 | 1580 | if (object == null) |
2728 | { | |
2729 | 0 | return NULL; |
2730 | } | |
2731 | 1580 | if (object instanceof JSONObject || object instanceof JSONArray |
2732 | || NULL.equals(object) || object instanceof JSONString | |
2733 | || object instanceof Byte || object instanceof Character | |
2734 | || object instanceof Short || object instanceof Integer | |
2735 | || object instanceof Long || object instanceof Boolean | |
2736 | || object instanceof Float || object instanceof Double | |
2737 | || object instanceof String || object instanceof BigInteger | |
2738 | || object instanceof BigDecimal || object instanceof Enum) | |
2739 | { | |
2740 | 1254 | return object; |
2741 | } | |
2742 | ||
2743 | 326 | if (object instanceof Collection) |
2744 | { | |
2745 | 43 | Collection<?> coll = (Collection<?>) object; |
2746 | 43 | return new JSONArray(coll); |
2747 | } | |
2748 | 283 | if (object.getClass().isArray()) |
2749 | { | |
2750 | 0 | return new JSONArray(object); |
2751 | } | |
2752 | 283 | if (object instanceof Map) |
2753 | { | |
2754 | 8 | Map<?, ?> map = (Map<?, ?>) object; |
2755 | 8 | return new JSONObject(map); |
2756 | } | |
2757 | 275 | Package objectPackage = object.getClass().getPackage(); |
2758 | 275 | String objectPackageName = objectPackage != null |
2759 | ? objectPackage.getName() | |
2760 | : ""; | |
2761 | 275 | if (objectPackageName.startsWith("java.") |
2762 | || objectPackageName.startsWith("javax.") | |
2763 | || object.getClass().getClassLoader() == null) | |
2764 | { | |
2765 | 0 | return object.toString(); |
2766 | } | |
2767 | 275 | return new JSONObject(object); |
2768 | } catch (Exception exception) | |
2769 | { | |
2770 | 0 | return null; |
2771 | } | |
2772 | } | |
2773 | ||
2774 | /** | |
2775 | * Write the contents of the JSONObject as JSON text to a writer. For | |
2776 | * compactness, no whitespace is added. | |
2777 | * <p> | |
2778 | * <b> Warning: This method assumes that the data structure is acyclical. </b> | |
2779 | * | |
2780 | * @return The writer. | |
2781 | * @throws JSONException | |
2782 | */ | |
2783 | 0 | public Writer write(Writer writer) throws JSONException |
2784 | { | |
2785 | 0 | return this.write(writer, 0, 0); |
2786 | } | |
2787 | ||
2788 | 1580 | static final Writer writeValue(Writer writer, Object value, |
2789 | int indentFactor, int indent) throws JSONException, IOException | |
2790 | { | |
2791 | 1580 | if (value == null || value.equals(null)) |
2792 | { | |
2793 | 0 | writer.write("null"); |
2794 | } | |
2795 | 1580 | else if (value instanceof JSONString) |
2796 | { | |
2797 | 0 | Object o; |
2798 | 0 | try |
2799 | { | |
2800 | 0 | o = ((JSONString) value).toJSONString(); |
2801 | } catch (Exception e) | |
2802 | { | |
2803 | 0 | throw new JSONException(e); |
2804 | } | |
2805 | 0 | writer.write(o != null ? o.toString() : quote(value.toString())); |
2806 | } | |
2807 | 1580 | else if (value instanceof Number) |
2808 | { | |
2809 | // not all Numbers may match actual JSON Numbers. i.e. fractions or | |
2810 | // Imaginary | |
2811 | 449 | final String numberAsString = numberToString((Number) value); |
2812 | 449 | try |
2813 | { | |
2814 | // Use the BigDecimal constructor for its parser to validate the format. | |
2815 | 449 | @SuppressWarnings("unused") |
2816 | BigDecimal testNum = new BigDecimal(numberAsString); | |
2817 | // Close enough to a JSON number that we will use it unquoted | |
2818 | 449 | writer.write(numberAsString); |
2819 | } catch (NumberFormatException ex) | |
2820 | { | |
2821 | // The Number value is not a valid JSON number. | |
2822 | // Instead we will quote it as a string | |
2823 | 0 | quote(numberAsString, writer); |
2824 | } | |
2825 | } | |
2826 | 1131 | else if (value instanceof Boolean) |
2827 | { | |
2828 | 54 | writer.write(value.toString()); |
2829 | } | |
2830 | 1077 | else if (value instanceof Enum<?>) |
2831 | { | |
2832 | 0 | writer.write(quote(((Enum<?>) value).name())); |
2833 | } | |
2834 | 1077 | else if (value instanceof JSONObject) |
2835 | { | |
2836 | 283 | ((JSONObject) value).write(writer, indentFactor, indent); |
2837 | } | |
2838 | 794 | else if (value instanceof JSONArray) |
2839 | { | |
2840 | 43 | ((JSONArray) value).write(writer, indentFactor, indent); |
2841 | } | |
2842 | 751 | else if (value instanceof Map) |
2843 | { | |
2844 | 0 | Map<?, ?> map = (Map<?, ?>) value; |
2845 | 0 | new JSONObject(map).write(writer, indentFactor, indent); |
2846 | } | |
2847 | 751 | else if (value instanceof Collection) |
2848 | { | |
2849 | 0 | Collection<?> coll = (Collection<?>) value; |
2850 | 0 | new JSONArray(coll).write(writer, indentFactor, indent); |
2851 | } | |
2852 | 751 | else if (value.getClass().isArray()) |
2853 | { | |
2854 | 0 | new JSONArray(value).write(writer, indentFactor, indent); |
2855 | } | |
2856 | else | |
2857 | { | |
2858 | 751 | quote(value.toString(), writer); |
2859 | } | |
2860 | 1580 | return writer; |
2861 | } | |
2862 | ||
2863 | 1885 | static final void indent(Writer writer, int indent) throws IOException |
2864 | { | |
2865 | 1885 | for (int i = 0; i < indent; i += 1) |
2866 | { | |
2867 | 0 | writer.write(' '); |
2868 | } | |
2869 | } | |
2870 | ||
2871 | /** | |
2872 | * Write the contents of the JSONObject as JSON text to a writer. | |
2873 | * | |
2874 | * <p> | |
2875 | * If <code>indentFactor > 0</code> and the {@link JSONObject} has only one | |
2876 | * key, then the object will be output on a single line: | |
2877 | * | |
2878 | * <pre> | |
2879 | * {@code {"key": 1}} | |
2880 | * </pre> | |
2881 | * | |
2882 | * <p> | |
2883 | * If an object has 2 or more keys, then it will be output across multiple | |
2884 | * lines: <code><pre>{ | |
2885 | * "key1": 1, | |
2886 | * "key2": "value 2", | |
2887 | * "key3": 3 | |
2888 | * }</pre></code> | |
2889 | * <p> | |
2890 | * <b> Warning: This method assumes that the data structure is acyclical. </b> | |
2891 | * | |
2892 | * @param writer | |
2893 | * Writes the serialized JSON | |
2894 | * @param indentFactor | |
2895 | * The number of spaces to add to each level of indentation. | |
2896 | * @param indent | |
2897 | * The indentation of the top level. | |
2898 | * @return The writer. | |
2899 | * @throws JSONException | |
2900 | */ | |
2901 | 291 | public Writer write(Writer writer, int indentFactor, int indent) |
2902 | throws JSONException | |
2903 | { | |
2904 | 291 | try |
2905 | { | |
2906 | 291 | boolean commanate = false; |
2907 | 291 | final int length = this.length(); |
2908 | 291 | writer.write('{'); |
2909 | ||
2910 | 291 | if (length == 1) |
2911 | { | |
2912 | 0 | final Entry<String, ?> entry = this.entrySet().iterator().next(); |
2913 | 0 | final String key = entry.getKey(); |
2914 | 0 | writer.write(quote(key)); |
2915 | 0 | writer.write(':'); |
2916 | 0 | if (indentFactor > 0) |
2917 | { | |
2918 | 0 | writer.write(' '); |
2919 | } | |
2920 | 0 | try |
2921 | { | |
2922 | 0 | writeValue(writer, entry.getValue(), indentFactor, indent); |
2923 | } catch (Exception e) | |
2924 | { | |
2925 | 0 | throw new JSONException( |
2926 | "Unable to write JSONObject value for key: " + key, e); | |
2927 | } | |
2928 | } | |
2929 | 291 | else if (length != 0) |
2930 | { | |
2931 | 291 | final int newindent = indent + indentFactor; |
2932 | 291 | for (final Entry<String, ?> entry : this.entrySet()) |
2933 | { | |
2934 | 1286 | if (commanate) |
2935 | { | |
2936 | 995 | writer.write(','); |
2937 | } | |
2938 | 1286 | if (indentFactor > 0) |
2939 | { | |
2940 | 0 | writer.write('\n'); |
2941 | } | |
2942 | 1286 | indent(writer, newindent); |
2943 | 1286 | final String key = entry.getKey(); |
2944 | 1286 | writer.write(quote(key)); |
2945 | 1286 | writer.write(':'); |
2946 | 1286 | if (indentFactor > 0) |
2947 | { | |
2948 | 0 | writer.write(' '); |
2949 | } | |
2950 | 1286 | try |
2951 | { | |
2952 | 1286 | writeValue(writer, entry.getValue(), indentFactor, newindent); |
2953 | } catch (Exception e) | |
2954 | { | |
2955 | 0 | throw new JSONException( |
2956 | "Unable to write JSONObject value for key: " + key, e); | |
2957 | } | |
2958 | 1286 | commanate = true; |
2959 | } | |
2960 | 291 | if (indentFactor > 0) |
2961 | { | |
2962 | 0 | writer.write('\n'); |
2963 | } | |
2964 | 291 | indent(writer, indent); |
2965 | } | |
2966 | 291 | writer.write('}'); |
2967 | 291 | return writer; |
2968 | } catch (IOException exception) | |
2969 | { | |
2970 | 0 | throw new JSONException(exception); |
2971 | } | |
2972 | } | |
2973 | ||
2974 | /** | |
2975 | * Returns a java.util.Map containing all of the entries in this object. If an | |
2976 | * entry in the object is a JSONArray or JSONObject it will also be converted. | |
2977 | * <p> | |
2978 | * Warning: This method assumes that the data structure is acyclical. | |
2979 | * | |
2980 | * @return a java.util.Map containing the entries of this object | |
2981 | */ | |
2982 | 0 | public Map<String, Object> toMap() |
2983 | { | |
2984 | 0 | Map<String, Object> results = new HashMap<String, Object>(); |
2985 | 0 | for (Entry<String, Object> entry : this.entrySet()) |
2986 | { | |
2987 | 0 | Object value; |
2988 | 0 | if (entry.getValue() == null || NULL.equals(entry.getValue())) |
2989 | { | |
2990 | 0 | value = null; |
2991 | } | |
2992 | 0 | else if (entry.getValue() instanceof JSONObject) |
2993 | { | |
2994 | 0 | value = ((JSONObject) entry.getValue()).toMap(); |
2995 | } | |
2996 | 0 | else if (entry.getValue() instanceof JSONArray) |
2997 | { | |
2998 | 0 | value = ((JSONArray) entry.getValue()).toList(); |
2999 | } | |
3000 | else | |
3001 | { | |
3002 | 0 | value = entry.getValue(); |
3003 | } | |
3004 | 0 | results.put(entry.getKey(), value); |
3005 | } | |
3006 | 0 | return results; |
3007 | } | |
3008 | } |