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