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