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 < ' ') || ((c >= '\u0080') && (c < '\u00a0')) || ((c >= '\u2000') && (c < '\u2100'))) { 591 t = "000" + Integer.toHexString(c); 592 sb.append("\\u" + t.substring(t.length() - 4)); 593 } else { 594 sb.append(c); 595 } 596 } 597 } 598 sb.append('"'); 599 return sb.toString(); 600 } 601 602 /** 603 * Throws an exception if the object is an NaN or infinite number.<p> 604 * 605 * @param o the object to test 606 * @throws JSONException if o is a non-finite number 607 */ 608 static void testValidity(Object o) throws JSONException { 609 610 if (o != null) { 611 if (o instanceof Double) { 612 if (((Double)o).isInfinite() || ((Double)o).isNaN()) { 613 throw new JSONException("JSON does not allow non-finite numbers."); 614 } 615 } else if (o instanceof Float) { 616 if (((Float)o).isInfinite() || ((Float)o).isNaN()) { 617 throw new JSONException("JSON does not allow non-finite numbers."); 618 } 619 } 620 } 621 } 622 623 /** 624 * Make a JSON text of an Object value.<p> 625 * 626 * If the object has an value.toJSONString() method, then that method will be used to produce 627 * the JSON text. The method is required to produce a strictly 628 * conforming text. If the object does not contain a toJSONString 629 * method (which is the most common case), then a text will be 630 * produced by other means. If the value is an array or Collection, 631 * then a JSONArray will be made from it and its toJSONString method 632 * will be called. If the value is a MAP, then a JSONObject will be made 633 * from it and its toJSONString method will be called. Otherwise, the 634 * value's toString method will be called, and the result will be quoted.<p> 635 * 636 * Warning: This method assumes that the data structure is acyclical.<p> 637 * 638 * @param value the value to be serialized 639 * @return a printable, displayable, transmittable 640 * representation of the object, beginning 641 * with <code>{</code> <small>(left brace)</small> and ending 642 * with <code>}</code> <small>(right brace)</small> 643 * @throws JSONException if the value is or contains an invalid number 644 */ 645 @SuppressWarnings("unchecked") 646 static String valueToString(Object value) throws JSONException { 647 648 if ((value == null) || value.equals(null)) { 649 return "null"; 650 } 651 if (value instanceof I_JSONString) { 652 Object o; 653 try { 654 o = ((I_JSONString)value).toJSONString(); 655 } catch (Exception e) { 656 throw new JSONException(e); 657 } 658 if (o instanceof String) { 659 return (String)o; 660 } 661 throw new JSONException("Bad value from toJSONString: " + o); 662 } 663 if (value instanceof Number) { 664 return numberToString((Number)value); 665 } 666 if ((value instanceof Boolean) || (value instanceof JSONObject) || (value instanceof JSONArray)) { 667 return value.toString(); 668 } 669 if (value instanceof Map) { 670 return new JSONObject((Map<String, Object>)value).toString(); 671 } 672 if (value instanceof Collection) { 673 return new JSONArray((Collection<Object>)value).toString(); 674 } 675 if (value.getClass().isArray()) { 676 return new JSONArray(value).toString(); 677 } 678 return quote(value.toString()); 679 } 680 681 /** 682 * Make a pretty printed JSON text of an object value.<p> 683 * 684 * Warning: This method assumes that the data structure is acyclical.<p> 685 * 686 * @param value the value to be serialized 687 * @param indentFactor the number of spaces to add to each level of 688 * indentation 689 * @param indent the indentation of the top level 690 * @return a printable, displayable, transmittable 691 * representation of the object, beginning 692 * with <code>{</code> <small>(left brace)</small> and ending 693 * with <code>}</code> <small>(right brace)</small> 694 * @throws JSONException if the object contains an invalid number 695 */ 696 @SuppressWarnings("unchecked") 697 static String valueToString(Object value, int indentFactor, int indent) throws JSONException { 698 699 if ((value == null) || value.equals(null)) { 700 return "null"; 701 } 702 try { 703 if (value instanceof I_JSONString) { 704 Object o = ((I_JSONString)value).toJSONString(); 705 if (o instanceof String) { 706 return (String)o; 707 } 708 } 709 } catch (Exception e) { 710 /* forget about it */ 711 } 712 if (value instanceof Number) { 713 return numberToString((Number)value); 714 } 715 if (value instanceof Boolean) { 716 return value.toString(); 717 } 718 if (value instanceof JSONObject) { 719 return ((JSONObject)value).toString(indentFactor, indent); 720 } 721 if (value instanceof JSONArray) { 722 return ((JSONArray)value).toString(indentFactor, indent); 723 } 724 if (value instanceof Map) { 725 return new JSONObject((Map<String, Object>)value).toString(indentFactor, indent); 726 } 727 if (value instanceof Collection) { 728 return new JSONArray((Collection<Object>)value).toString(indentFactor, indent); 729 } 730 if (value.getClass().isArray()) { 731 return new JSONArray(value).toString(indentFactor, indent); 732 } 733 return quote(value.toString()); 734 } 735 736 /** 737 * Accumulate values under a key.<p> 738 * 739 * It is similar to the put method except 740 * that if there is already an object stored under the key then a 741 * JSONArray is stored under the key to hold all of the accumulated values. 742 * If there is already a JSONArray, then the new value is appended to it. 743 * In contrast, the put method replaces the previous value.<p> 744 * 745 * @param key a key string 746 * @param value an object to be accumulated under the key 747 * @return this 748 * @throws JSONException if the value is an invalid number or if the key is null 749 */ 750 public JSONObject accumulate(String key, Object value) throws JSONException { 751 752 testValidity(value); 753 Object o = opt(key); 754 if (o == null) { 755 put(key, value instanceof JSONArray ? new JSONArray().put(value) : value); 756 } else if (o instanceof JSONArray) { 757 ((JSONArray)o).put(value); 758 } else { 759 put(key, new JSONArray().put(o).put(value)); 760 } 761 return this; 762 } 763 764 /** 765 * Append values to the array under a key.<p> 766 * 767 * If the key does not exist in the 768 * JSONObject, then the key is put in the JSONObject with its value being a 769 * JSONArray containing the value parameter. If the key was already 770 * associated with a JSONArray, then the value parameter is appended to it.<p> 771 * 772 * @param key a key string 773 * @param value an object to be accumulated under the key 774 * @return this 775 * @throws JSONException if the key is null or if the current value 776 * associated with the key is not a JSONArray 777 */ 778 public JSONObject append(String key, Object value) throws JSONException { 779 780 testValidity(value); 781 Object o = opt(key); 782 if (o == null) { 783 put(key, new JSONArray().put(value)); 784 } else if (o instanceof JSONArray) { 785 put(key, ((JSONArray)o).put(value)); 786 } else { 787 throw new JSONException("JSONObject[" + key + "] is not a JSONArray."); 788 } 789 return this; 790 } 791 792 /** 793 * Get the value object associated with a key.<p> 794 * 795 * @param key a key string 796 * @return the object associated with the key 797 * @throws JSONException if the key is not found 798 */ 799 public Object get(String key) throws JSONException { 800 801 Object o = opt(key); 802 if (o == null) { 803 throw new JSONException("JSONObject[" + quote(key) + "] not found."); 804 } 805 return o; 806 } 807 808 /** 809 * Get the boolean value associated with a key.<p> 810 * 811 * @param key A key string 812 * @return the truth 813 * @throws JSONException if the value is not a Boolean or the String "true" or "false" 814 */ 815 public boolean getBoolean(String key) throws JSONException { 816 817 Object o = get(key); 818 if (o.equals(Boolean.FALSE) || ((o instanceof String) && ((String)o).equalsIgnoreCase("false"))) { 819 return false; 820 } else if (o.equals(Boolean.TRUE) || ((o instanceof String) && ((String)o).equalsIgnoreCase("true"))) { 821 return true; 822 } 823 throw new JSONException("JSONObject[" + quote(key) + "] is not a Boolean."); 824 } 825 826 /** 827 * Get the double value associated with a key.<p> 828 * 829 * @param key a key string 830 * @return the numeric value 831 * @throws JSONException if the key is not found or 832 * if the value is not a Number object and cannot be converted to a number 833 */ 834 public double getDouble(String key) throws JSONException { 835 836 Object o = get(key); 837 try { 838 return o instanceof Number ? ((Number)o).doubleValue() : Double.valueOf((String)o).doubleValue(); 839 } catch (Exception e) { 840 throw new JSONException("JSONObject[" + quote(key) + "] is not a number."); 841 } 842 } 843 844 /** 845 * Get the int value associated with a key.<p> 846 * 847 * If the number value is too large for an int, it will be clipped.<p> 848 * 849 * @param key a key string 850 * @return the integer value 851 * @throws JSONException if the key is not found or if the value cannot 852 * be converted to an integer 853 */ 854 public int getInt(String key) throws JSONException { 855 856 Object o = get(key); 857 return o instanceof Number ? ((Number)o).intValue() : (int)getDouble(key); 858 } 859 860 /** 861 * Get the JSONArray value associated with a key.<p> 862 * 863 * @param key a key string 864 * @return a JSONArray which is the value 865 * @throws JSONException if the key is not found or 866 * if the value is not a JSONArray 867 */ 868 public JSONArray getJSONArray(String key) throws JSONException { 869 870 Object o = get(key); 871 if (o instanceof JSONArray) { 872 return (JSONArray)o; 873 } 874 throw new JSONException("JSONObject[" + quote(key) + "] is not a JSONArray."); 875 } 876 877 /** 878 * Get the JSONObject value associated with a key.<p> 879 * 880 * @param key a key string 881 * @return a JSONObject which is the value 882 * @throws JSONException if the key is not found or 883 * if the value is not a JSONObject 884 */ 885 public JSONObject getJSONObject(String key) throws JSONException { 886 887 Object o = get(key); 888 if (o instanceof JSONObject) { 889 return (JSONObject)o; 890 } 891 throw new JSONException("JSONObject[" + quote(key) + "] is not a JSONObject."); 892 } 893 894 /** 895 * Get the long value associated with a key.<p> 896 * 897 * If the number value is too long for a long, it will be clipped.<p> 898 * 899 * @param key a key string 900 * @return the long value. 901 * @throws JSONException if the key is not found or if the value cannot 902 * be converted to a long 903 */ 904 public long getLong(String key) throws JSONException { 905 906 Object o = get(key); 907 return o instanceof Number ? ((Number)o).longValue() : (long)getDouble(key); 908 } 909 910 /** 911 * Get the string associated with a key.<p> 912 * 913 * @param key a key string 914 * @return a string which is the value 915 * @throws JSONException if the key is not found 916 */ 917 public String getString(String key) throws JSONException { 918 919 return get(key).toString(); 920 } 921 922 /** 923 * Determine if the JSONObject contains a specific key.<p> 924 * 925 * @param key a key string 926 * @return true if the key exists in the JSONObject 927 */ 928 public boolean has(String key) { 929 930 return m_map.containsKey(key); 931 } 932 933 /** 934 * Determine if the value associated with the key is null or if there is no value.<p> 935 * 936 * @param key a key string 937 * @return true if there is no value associated with the key or if 938 * the value is the JSONObject.NULL object 939 */ 940 public boolean isNull(String key) { 941 942 return JSONObject.NULL.equals(opt(key)); 943 } 944 945 /** 946 * Get an enumeration of the keys of the JSONObject.<p> 947 * 948 * @return an iterator of the keys 949 */ 950 public Iterator<String> keys() { 951 952 return m_map.keySet().iterator(); 953 } 954 955 /** 956 * Gets the set of keys.<p> 957 * 958 * @return the set of keys 959 */ 960 public Set<String> keySet() { 961 962 return m_map.keySet(); 963 } 964 965 /** 966 * Get the number of keys stored in the JSONObject.<p> 967 * 968 * @return The number of keys in the JSONObject 969 */ 970 public int length() { 971 972 return m_map.size(); 973 } 974 975 /** 976 * Merges the current JSON object with the given one, modifying the this.<p> 977 * 978 * @param jo the JSON object to merge 979 * @param overwrite if to overwrite values 980 * @param deep if to recurse in object values 981 * 982 * @throws JSONException if a value is a non-finite number 983 * 984 * @since 7.6 985 */ 986 public void merge(JSONObject jo, boolean overwrite, boolean deep) throws JSONException { 987 988 Iterator<String> it = jo.keys(); 989 while (it.hasNext()) { 990 String key = it.next(); 991 if (!has(key)) { 992 put(key, jo.get(key)); 993 continue; 994 } 995 boolean recurse = deep && (jo.optJSONObject(key) != null) && (optJSONObject(key) != null); 996 if (overwrite && !recurse) { 997 put(key, jo.get(key)); 998 continue; 999 } 1000 if (recurse) { 1001 getJSONObject(key).merge(jo.getJSONObject(key), overwrite, deep); 1002 } 1003 } 1004 } 1005 1006 /** 1007 * Produce a JSONArray containing the names of the elements of this JSONObject.<p> 1008 * 1009 * @return a JSONArray containing the key strings, or null if the JSONObject is empty. 1010 */ 1011 public JSONArray names() { 1012 1013 JSONArray ja = new JSONArray(); 1014 Iterator<String> keys = keys(); 1015 while (keys.hasNext()) { 1016 ja.put(keys.next()); 1017 } 1018 return ja.length() == 0 ? null : ja; 1019 } 1020 1021 /** 1022 * Get an optional value associated with a key.<p> 1023 * 1024 * @param key a key string 1025 * @return an object which is the value, or null if there is no value 1026 */ 1027 public Object opt(String key) { 1028 1029 return key == null ? null : m_map.get(key); 1030 } 1031 1032 /** 1033 * Get an optional boolean associated with a key.<p> 1034 * 1035 * It returns false if there is no such key, or if the value is not 1036 * Boolean.TRUE or the String "true".<p> 1037 * 1038 * @param key a key string 1039 * @return the truth 1040 */ 1041 public boolean optBoolean(String key) { 1042 1043 return optBoolean(key, false); 1044 } 1045 1046 /** 1047 * Get an optional boolean associated with a key.<p> 1048 * 1049 * It returns the defaultValue if there is no such key, or if it is not 1050 * a Boolean or the String "true" or "false" (case insensitive).<p> 1051 * 1052 * @param key a key string 1053 * @param defaultValue the default 1054 * @return the truth 1055 */ 1056 public boolean optBoolean(String key, boolean defaultValue) { 1057 1058 try { 1059 return getBoolean(key); 1060 } catch (Exception e) { 1061 return defaultValue; 1062 } 1063 } 1064 1065 /** 1066 * Get an optional double associated with a key, 1067 * or NaN if there is no such key or if its value is not a number.<p> 1068 * 1069 * If the value is a string, an attempt will be made to evaluate it as 1070 * a number.<p> 1071 * 1072 * @param key a string which is the key 1073 * @return an object which is the value 1074 */ 1075 public double optDouble(String key) { 1076 1077 return optDouble(key, Double.NaN); 1078 } 1079 1080 /** 1081 * Get an optional double associated with a key, or the 1082 * defaultValue if there is no such key or if its value is not a number.<p> 1083 * 1084 * If the value is a string, an attempt will be made to evaluate it as 1085 * a number.<p> 1086 * 1087 * @param key a key string 1088 * @param defaultValue the default 1089 * @return an object which is the value 1090 */ 1091 public double optDouble(String key, double defaultValue) { 1092 1093 try { 1094 Object o = opt(key); 1095 return o instanceof Number ? ((Number)o).doubleValue() : new Double((String)o).doubleValue(); 1096 } catch (Exception e) { 1097 return defaultValue; 1098 } 1099 } 1100 1101 /** 1102 * Get an optional int value associated with a key, 1103 * or zero if there is no such key or if the value is not a number.<p> 1104 * 1105 * If the value is a string, an attempt will be made to evaluate it as 1106 * a number.<p> 1107 * 1108 * @param key a key string 1109 * @return an object which is the value 1110 */ 1111 public int optInt(String key) { 1112 1113 return optInt(key, 0); 1114 } 1115 1116 /** 1117 * Get an optional int value associated with a key, 1118 * or the default if there is no such key or if the value is not a number.<p> 1119 * 1120 * If the value is a string, an attempt will be made to evaluate it as 1121 * a number.<p> 1122 * 1123 * @param key a key string 1124 * @param defaultValue the default 1125 * @return an object which is the value 1126 */ 1127 public int optInt(String key, int defaultValue) { 1128 1129 try { 1130 return getInt(key); 1131 } catch (Exception e) { 1132 return defaultValue; 1133 } 1134 } 1135 1136 /** 1137 * Get an optional JSONArray associated with a key.<p> 1138 * 1139 * It returns null if there is no such key, or if its value is not a 1140 * JSONArray.<p> 1141 * 1142 * @param key a key string 1143 * @return a JSONArray which is the value 1144 */ 1145 public JSONArray optJSONArray(String key) { 1146 1147 Object o = opt(key); 1148 return o instanceof JSONArray ? (JSONArray)o : null; 1149 } 1150 1151 /** 1152 * Get an optional JSONObject associated with a key.<p> 1153 * 1154 * It returns null if there is no such key, or if its value is not a 1155 * JSONObject.<p> 1156 * 1157 * @param key a key string 1158 * @return a JSONObject which is the value 1159 */ 1160 public JSONObject optJSONObject(String key) { 1161 1162 Object o = opt(key); 1163 return o instanceof JSONObject ? (JSONObject)o : null; 1164 } 1165 1166 /** 1167 * Get an optional long value associated with a key, 1168 * or zero if there is no such key or if the value is not a number.<p> 1169 * 1170 * If the value is a string, an attempt will be made to evaluate it as 1171 * a number.<p> 1172 * 1173 * @param key a key string 1174 * @return an object which is the value 1175 */ 1176 public long optLong(String key) { 1177 1178 return optLong(key, 0); 1179 } 1180 1181 /** 1182 * Get an optional long value associated with a key, 1183 * or the default if there is no such key or if the value is not a number.<p> 1184 * 1185 * If the value is a string, an attempt will be made to evaluate it as 1186 * a number.<p> 1187 * 1188 * @param key a key string 1189 * @param defaultValue the default 1190 * @return an object which is the value 1191 */ 1192 public long optLong(String key, long defaultValue) { 1193 1194 try { 1195 return getLong(key); 1196 } catch (Exception e) { 1197 return defaultValue; 1198 } 1199 } 1200 1201 /** 1202 * Get an optional string associated with a key.<p> 1203 * 1204 * It returns an empty string if there is no such key. If the value is not 1205 * a string and is not null, then it is coverted to a string.<p> 1206 * 1207 * @param key a key string 1208 * @return a string which is the value 1209 */ 1210 public String optString(String key) { 1211 1212 return optString(key, ""); 1213 } 1214 1215 /** 1216 * Get an optional string associated with a key. 1217 * It returns the defaultValue if there is no such key.<p> 1218 * 1219 * @param key a key string 1220 * @param defaultValue the default 1221 * @return a string which is the value 1222 */ 1223 public String optString(String key, String defaultValue) { 1224 1225 Object o = opt(key); 1226 return o != null ? o.toString() : defaultValue; 1227 } 1228 1229 /** 1230 * Put a key/boolean pair in the JSONObject.<p> 1231 * 1232 * @param key a key string 1233 * @param value a boolean which is the value 1234 * @return this 1235 * @throws JSONException if the key is null 1236 */ 1237 public JSONObject put(String key, boolean value) throws JSONException { 1238 1239 put(key, value ? Boolean.TRUE : Boolean.FALSE); 1240 return this; 1241 } 1242 1243 /** 1244 * Put a key/value pair in the JSONObject, where the value will be a 1245 * JSONArray which is produced from a Collection.<p> 1246 * 1247 * @param key a key string 1248 * @param value a Collection value 1249 * @return this 1250 * @throws JSONException if something goes wrong 1251 */ 1252 public JSONObject put(String key, Collection<Object> value) throws JSONException { 1253 1254 put(key, new JSONArray(value)); 1255 return this; 1256 } 1257 1258 /** 1259 * Put a key/double pair in the JSONObject.<p> 1260 * 1261 * @param key a key string 1262 * @param value a double which is the value 1263 * @return this 1264 * @throws JSONException if the key is null or if the number is invalid. 1265 */ 1266 public JSONObject put(String key, double value) throws JSONException { 1267 1268 put(key, new Double(value)); 1269 return this; 1270 } 1271 1272 /** 1273 * Put a key/int pair in the JSONObject.<p> 1274 * 1275 * @param key a key string 1276 * @param value an int which is the value 1277 * @return this 1278 * @throws JSONException if the key is null 1279 */ 1280 public JSONObject put(String key, int value) throws JSONException { 1281 1282 put(key, new Integer(value)); 1283 return this; 1284 } 1285 1286 /** 1287 * Put a key/long pair in the JSONObject.<p> 1288 * 1289 * @param key a key string 1290 * @param value a long which is the value 1291 * @return this 1292 * @throws JSONException If the key is null 1293 */ 1294 public JSONObject put(String key, long value) throws JSONException { 1295 1296 put(key, new Long(value)); 1297 return this; 1298 } 1299 1300 /** 1301 * Put a key/value pair in the JSONObject, where the value will be a 1302 * JSONObject which is produced from a Map.<p> 1303 * 1304 * @param key a key string 1305 * @param value a Map value 1306 * @return this 1307 * @throws JSONException if something goes wrong 1308 */ 1309 public JSONObject put(String key, Map<String, Object> value) throws JSONException { 1310 1311 put(key, new JSONObject(value)); 1312 return this; 1313 } 1314 1315 /** 1316 * Put a key/value pair in the JSONObject.<p> 1317 * 1318 * If the value is null, 1319 * then the key will be removed from the JSONObject if it is present.<p> 1320 * 1321 * @param key a key string 1322 * @param value an object which is the value. It should be of one of these 1323 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, 1324 * or the JSONObject.NULL object 1325 * @return this 1326 * @throws JSONException if the value is non-finite number 1327 * or if the key is null. 1328 */ 1329 public JSONObject put(String key, Object value) throws JSONException { 1330 1331 if (key == null) { 1332 throw new JSONException("Null key."); 1333 } 1334 if (value != null) { 1335 testValidity(value); 1336 m_map.put(key, value); 1337 } else { 1338 remove(key); 1339 } 1340 return this; 1341 } 1342 1343 /** 1344 * Put a key/value pair in the JSONObject, but only if the 1345 * key and the value are both non-null.<p> 1346 * 1347 * @param key a key string 1348 * @param value an object which is the value. It should be of one of these 1349 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, 1350 * or the JSONObject.NULL object 1351 * @return this 1352 * @throws JSONException if the value is a non-finite number. 1353 */ 1354 public JSONObject putOpt(String key, Object value) throws JSONException { 1355 1356 if ((key != null) && (value != null)) { 1357 put(key, value); 1358 } 1359 return this; 1360 } 1361 1362 /** 1363 * Remove a name and its value, if present.<p> 1364 * 1365 * @param key the name to be removed 1366 * @return the value that was associated with the name, 1367 * or null if there was no value 1368 */ 1369 public Object remove(String key) { 1370 1371 return m_map.remove(key); 1372 } 1373 1374 /** 1375 * Get an enumeration of the keys of the JSONObject.<p> 1376 * 1377 * The keys will be sorted alphabetically.<p> 1378 * 1379 * @return an iterator of the keys 1380 */ 1381 public Iterator<String> sortedKeys() { 1382 1383 return new TreeSet<String>(m_map.keySet()).iterator(); 1384 } 1385 1386 /** 1387 * Produce a JSONArray containing the values of the members of this 1388 * JSONObject.<p> 1389 * 1390 * @param names a JSONArray containing a list of key strings. This 1391 * determines the sequence of the values in the result 1392 * @return a JSONArray of values 1393 * @throws JSONException if any of the values are non-finite numbers. 1394 */ 1395 public JSONArray toJSONArray(JSONArray names) throws JSONException { 1396 1397 if ((names == null) || (names.length() == 0)) { 1398 return null; 1399 } 1400 JSONArray ja = new JSONArray(); 1401 for (int i = 0; i < names.length(); i += 1) { 1402 ja.put(opt(names.getString(i))); 1403 } 1404 return ja; 1405 } 1406 1407 /** 1408 * Make a JSON text of this JSONObject.<p> 1409 * 1410 * For compactness, no whitespace 1411 * is added. If this would not result in a syntactically correct JSON text, 1412 * then null will be returned instead.<p> 1413 * 1414 * Warning: This method assumes that the data structure is acyclical.<p> 1415 * 1416 * @return a printable, displayable, portable, transmittable 1417 * representation of the object, beginning 1418 * with <code>{</code> <small>(left brace)</small> and ending 1419 * with <code>}</code> <small>(right brace)</small>. 1420 */ 1421 @Override 1422 public String toString() { 1423 1424 try { 1425 Iterator<String> keys = keys(); 1426 StringBuffer sb = new StringBuffer("{"); 1427 1428 while (keys.hasNext()) { 1429 if (sb.length() > 1) { 1430 sb.append(','); 1431 } 1432 Object o = keys.next(); 1433 sb.append(quote(o.toString())); 1434 sb.append(':'); 1435 sb.append(valueToString(m_map.get(o))); 1436 } 1437 sb.append('}'); 1438 return sb.toString(); 1439 } catch (Exception e) { 1440 return null; 1441 } 1442 } 1443 1444 /** 1445 * Make a pretty printed JSON text of this JSONObject.<p> 1446 * 1447 * Warning: This method assumes that the data structure is acyclical.<p> 1448 * 1449 * @param indentFactor the number of spaces to add to each level of 1450 * indentation 1451 * @return a printable, displayable, portable, transmittable 1452 * representation of the object, beginning 1453 * with <code>{</code> <small>(left brace)</small> and ending 1454 * with <code>}</code> <small>(right brace)</small> 1455 * @throws JSONException If the object contains an invalid number 1456 */ 1457 public String toString(int indentFactor) throws JSONException { 1458 1459 return toString(indentFactor, 0); 1460 } 1461 1462 /** 1463 * Write the contents of the JSONObject as JSON text to a writer. 1464 * For compactness, no whitespace is added.<p> 1465 * 1466 * Warning: This method assumes that the data structure is acyclical.<p> 1467 * 1468 * @param writer the writer to write the contents to 1469 * @return the writer 1470 * @throws JSONException if something goes wrong 1471 */ 1472 public Writer write(Writer writer) throws JSONException { 1473 1474 try { 1475 boolean b = false; 1476 Iterator<String> keys = keys(); 1477 writer.write('{'); 1478 1479 while (keys.hasNext()) { 1480 if (b) { 1481 writer.write(','); 1482 } 1483 String k = keys.next(); 1484 writer.write(quote(k.toString())); 1485 writer.write(':'); 1486 Object v = m_map.get(k); 1487 if (v instanceof JSONObject) { 1488 ((JSONObject)v).write(writer); 1489 } else if (v instanceof JSONArray) { 1490 ((JSONArray)v).write(writer); 1491 } else { 1492 writer.write(valueToString(v)); 1493 } 1494 b = true; 1495 } 1496 writer.write('}'); 1497 return writer; 1498 } catch (IOException e) { 1499 throw new JSONException(e); 1500 } 1501 } 1502 1503 /** 1504 * Make a pretty printed JSON text of this JSONObject.<p> 1505 * 1506 * Warning: This method assumes that the data structure is acyclical.<p> 1507 * 1508 * @param indentFactor the number of spaces to add to each level of 1509 * indentation 1510 * @param indent the indentation of the top level 1511 * @return a printable, displayable, transmittable 1512 * representation of the object, beginning 1513 * with <code>{</code> <small>(left brace)</small> and ending 1514 * with <code>}</code> <small>(right brace)</small> 1515 * @throws JSONException if the object contains an invalid number 1516 */ 1517 String toString(int indentFactor, int indent) throws JSONException { 1518 1519 int j; 1520 int n = length(); 1521 if (n == 0) { 1522 return "{}"; 1523 } 1524 Iterator<String> keys = sortedKeys(); 1525 StringBuffer sb = new StringBuffer("{"); 1526 int newindent = indent + indentFactor; 1527 String key; 1528 if (n == 1) { 1529 key = keys.next(); 1530 sb.append(quote(key)); 1531 sb.append(": "); 1532 sb.append(valueToString(m_map.get(key), indentFactor, indent)); 1533 } else { 1534 while (keys.hasNext()) { 1535 key = keys.next(); 1536 if (sb.length() > 1) { 1537 sb.append(",\n"); 1538 } else { 1539 sb.append('\n'); 1540 } 1541 for (j = 0; j < newindent; j += 1) { 1542 sb.append(' '); 1543 } 1544 sb.append(quote(key)); 1545 sb.append(": "); 1546 sb.append(valueToString(m_map.get(key), indentFactor, newindent)); 1547 } 1548 if (sb.length() > 1) { 1549 sb.append('\n'); 1550 for (j = 0; j < indent; j += 1) { 1551 sb.append(' '); 1552 } 1553 } 1554 } 1555 sb.append('}'); 1556 return sb.toString(); 1557 } 1558 1559 /** 1560 * Returns if the given class is a standard property.<p> 1561 * 1562 * @param clazz the class 1563 * 1564 * @return <code>true</code> if the given class is a standard property 1565 */ 1566 private boolean isStandardProperty(Class<?> clazz) { 1567 1568 return clazz.isPrimitive() 1569 || clazz.isAssignableFrom(Byte.class) 1570 || clazz.isAssignableFrom(Short.class) 1571 || clazz.isAssignableFrom(Integer.class) 1572 || clazz.isAssignableFrom(Long.class) 1573 || clazz.isAssignableFrom(Float.class) 1574 || clazz.isAssignableFrom(Double.class) 1575 || clazz.isAssignableFrom(Character.class) 1576 || clazz.isAssignableFrom(String.class) 1577 || clazz.isAssignableFrom(Boolean.class); 1578 } 1579 1580 /** 1581 * Populates the internal map.<p> 1582 * 1583 * @param bean the bean 1584 * @param includeSuperClass flag indicating if super class properties should be included 1585 */ 1586 @SuppressWarnings("unchecked") 1587 private void populateInternalMap(Object bean, boolean includeSuperClass) { 1588 1589 Class<?> klass = bean.getClass(); 1590 1591 //If klass.getSuperClass is System class then includeSuperClass = false; 1592 1593 if (klass.getClassLoader() == null) { 1594 includeSuperClass = false; 1595 } 1596 1597 Method[] methods = (includeSuperClass) ? klass.getMethods() : klass.getDeclaredMethods(); 1598 for (int i = 0; i < methods.length; i += 1) { 1599 try { 1600 Method method = methods[i]; 1601 String name = method.getName(); 1602 String key = ""; 1603 if (name.startsWith("get")) { 1604 key = name.substring(3); 1605 } else if (name.startsWith("is")) { 1606 key = name.substring(2); 1607 } 1608 if ((key.length() > 0) 1609 && Character.isUpperCase(key.charAt(0)) 1610 && (method.getParameterTypes().length == 0)) { 1611 if (key.length() == 1) { 1612 key = key.toLowerCase(); 1613 } else if (!Character.isUpperCase(key.charAt(1))) { 1614 key = key.substring(0, 1).toLowerCase() + key.substring(1); 1615 } 1616 1617 Object result = method.invoke(bean, (Object[])null); 1618 if (result == null) { 1619 m_map.put(key, NULL); 1620 } else if (result.getClass().isArray()) { 1621 m_map.put(key, new JSONArray(result, includeSuperClass)); 1622 } else if (result instanceof Collection) { //List or Set 1623 m_map.put(key, new JSONArray((Collection<Object>)result, includeSuperClass)); 1624 } else if (result instanceof Map) { 1625 m_map.put(key, new JSONObject((Map<String, Object>)result, includeSuperClass)); 1626 } else if (isStandardProperty(result.getClass())) { //Primitives, String and Wrapper 1627 m_map.put(key, result); 1628 } else { 1629 if (result.getClass().getPackage().getName().startsWith("java") 1630 || (result.getClass().getClassLoader() == null)) { 1631 m_map.put(key, result.toString()); 1632 } else { //User defined Objects 1633 m_map.put(key, new JSONObject(result, includeSuperClass)); 1634 } 1635 } 1636 } 1637 } catch (Exception e) { 1638 throw new RuntimeException(e); 1639 } 1640 } 1641 } 1642}