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