1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.json; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.Iterator; 22 import java.util.LinkedHashMap; 23 import java.util.Map; 24 import java.util.Set; 25 26 // Note: this class was written without inspecting the non-free org.json sourcecode. 27 28 /** 29 * A modifiable set of name/value mappings. Names are unique, non-null strings. 30 * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray 31 * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}. 32 * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link 33 * Double#isInfinite() infinities}, or of any type not listed here. 34 * 35 * <p>This class can coerce values to another type when requested. 36 * <ul> 37 * <li>When the requested type is a boolean, strings will be coerced using a 38 * case-insensitive comparison to "true" and "false". 39 * <li>When the requested type is a double, other {@link Number} types will 40 * be coerced using {@link Number#doubleValue() doubleValue}. Strings 41 * that can be coerced using {@link Double#valueOf(String)} will be. 42 * <li>When the requested type is an int, other {@link Number} types will 43 * be coerced using {@link Number#intValue() intValue}. Strings 44 * that can be coerced using {@link Double#valueOf(String)} will be, 45 * and then cast to int. 46 * <li><a name="lossy">When the requested type is a long, other {@link Number} types will 47 * be coerced using {@link Number#longValue() longValue}. Strings 48 * that can be coerced using {@link Double#valueOf(String)} will be, 49 * and then cast to long. This two-step conversion is lossy for very 50 * large values. For example, the string "9223372036854775806" yields the 51 * long 9223372036854775807.</a> 52 * <li>When the requested type is a String, other non-null values will be 53 * coerced using {@link String#valueOf(Object)}. Although null cannot be 54 * coerced, the sentinel value {@link JSONObject#NULL} is coerced to the 55 * string "null". 56 * </ul> 57 * 58 * <p>This class can look up both mandatory and optional values: 59 * <ul> 60 * <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This 61 * fails with a {@code JSONException} if the requested name has no value 62 * or if the value cannot be coerced to the requested type. 63 * <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This 64 * returns a system- or user-supplied default if the requested name has no 65 * value or if the value cannot be coerced to the requested type. 66 * </ul> 67 * 68 * <p><strong>Warning:</strong> this class represents null in two incompatible 69 * ways: the standard Java {@code null} reference, and the sentinel value {@link 70 * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the 71 * named entry from the object but {@code put(name, JSONObject.NULL)} stores an 72 * entry whose value is {@code JSONObject.NULL}. 73 * 74 * <p>Instances of this class are not thread safe. Although this class is 75 * nonfinal, it was not designed for inheritance and should not be subclassed. 76 * In particular, self-use by overrideable methods is not specified. See 77 * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else 78 * prohibit it" for further information. 79 */ 80 public class JSONObject { 81 82 private static final Double NEGATIVE_ZERO = -0d; 83 84 /** 85 * A sentinel value used to explicitly define a name with no value. Unlike 86 * {@code null}, names with this value: 87 * <ul> 88 * <li>show up in the {@link #names} array 89 * <li>show up in the {@link #keys} iterator 90 * <li>return {@code true} for {@link #has(String)} 91 * <li>do not throw on {@link #get(String)} 92 * <li>are included in the encoded JSON string. 93 * </ul> 94 * 95 * <p>This value violates the general contract of {@link Object#equals} by 96 * returning true when compared to {@code null}. Its {@link #toString} 97 * method returns "null". 98 */ 99 public static final Object NULL = new Object() { 100 @Override public boolean equals(Object o) { 101 return o == this || o == null; // API specifies this broken equals implementation 102 } 103 @Override public String toString() { 104 return "null"; 105 } 106 }; 107 108 private final LinkedHashMap<String, Object> nameValuePairs; 109 110 /** 111 * Creates a {@code JSONObject} with no name/value mappings. 112 */ JSONObject()113 public JSONObject() { 114 nameValuePairs = new LinkedHashMap<String, Object>(); 115 } 116 117 /** 118 * Creates a new {@code JSONObject} by copying all name/value mappings from 119 * the given map. 120 * 121 * @param copyFrom a map whose keys are of type {@link String} and whose 122 * values are of supported types. 123 * @throws NullPointerException if any of the map's keys are null. 124 */ 125 /* (accept a raw type for API compatibility) */ JSONObject(Map copyFrom)126 public JSONObject(Map copyFrom) { 127 this(); 128 Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom; 129 for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) { 130 /* 131 * Deviate from the original by checking that keys are non-null and 132 * of the proper type. (We still defer validating the values). 133 */ 134 String key = (String) entry.getKey(); 135 if (key == null) { 136 throw new NullPointerException("key == null"); 137 } 138 nameValuePairs.put(key, wrap(entry.getValue())); 139 } 140 } 141 142 /** 143 * Creates a new {@code JSONObject} with name/value mappings from the next 144 * object in the tokener. 145 * 146 * @param readFrom a tokener whose nextValue() method will yield a 147 * {@code JSONObject}. 148 * @throws JSONException if the parse fails or doesn't yield a 149 * {@code JSONObject}. 150 */ JSONObject(JSONTokener readFrom)151 public JSONObject(JSONTokener readFrom) throws JSONException { 152 /* 153 * Getting the parser to populate this could get tricky. Instead, just 154 * parse to temporary JSONObject and then steal the data from that. 155 */ 156 Object object = readFrom.nextValue(); 157 if (object instanceof JSONObject) { 158 this.nameValuePairs = ((JSONObject) object).nameValuePairs; 159 } else { 160 throw JSON.typeMismatch(object, "JSONObject"); 161 } 162 } 163 164 /** 165 * Creates a new {@code JSONObject} with name/value mappings from the JSON 166 * string. 167 * 168 * @param json a JSON-encoded string containing an object. 169 * @throws JSONException if the parse fails or doesn't yield a {@code 170 * JSONObject}. 171 */ JSONObject(String json)172 public JSONObject(String json) throws JSONException { 173 this(new JSONTokener(json)); 174 } 175 176 /** 177 * Creates a new {@code JSONObject} by copying mappings for the listed names 178 * from the given object. Names that aren't present in {@code copyFrom} will 179 * be skipped. 180 */ JSONObject(JSONObject copyFrom, String[] names)181 public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { 182 this(); 183 for (String name : names) { 184 Object value = copyFrom.opt(name); 185 if (value != null) { 186 nameValuePairs.put(name, value); 187 } 188 } 189 } 190 191 /** 192 * Returns the number of name/value mappings in this object. 193 */ length()194 public int length() { 195 return nameValuePairs.size(); 196 } 197 198 /** 199 * Maps {@code name} to {@code value}, clobbering any existing name/value 200 * mapping with the same name. 201 * 202 * @return this object. 203 */ put(String name, boolean value)204 public JSONObject put(String name, boolean value) throws JSONException { 205 nameValuePairs.put(checkName(name), value); 206 return this; 207 } 208 209 /** 210 * Maps {@code name} to {@code value}, clobbering any existing name/value 211 * mapping with the same name. 212 * 213 * @param value a finite value. May not be {@link Double#isNaN() NaNs} or 214 * {@link Double#isInfinite() infinities}. 215 * @return this object. 216 */ put(String name, double value)217 public JSONObject put(String name, double value) throws JSONException { 218 nameValuePairs.put(checkName(name), JSON.checkDouble(value)); 219 return this; 220 } 221 222 /** 223 * Maps {@code name} to {@code value}, clobbering any existing name/value 224 * mapping with the same name. 225 * 226 * @return this object. 227 */ put(String name, int value)228 public JSONObject put(String name, int value) throws JSONException { 229 nameValuePairs.put(checkName(name), value); 230 return this; 231 } 232 233 /** 234 * Maps {@code name} to {@code value}, clobbering any existing name/value 235 * mapping with the same name. 236 * 237 * @return this object. 238 */ put(String name, long value)239 public JSONObject put(String name, long value) throws JSONException { 240 nameValuePairs.put(checkName(name), value); 241 return this; 242 } 243 244 /** 245 * Maps {@code name} to {@code value}, clobbering any existing name/value 246 * mapping with the same name. If the value is {@code null}, any existing 247 * mapping for {@code name} is removed. 248 * 249 * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, 250 * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be 251 * {@link Double#isNaN() NaNs} or {@link Double#isInfinite() 252 * infinities}. 253 * @return this object. 254 */ put(String name, Object value)255 public JSONObject put(String name, Object value) throws JSONException { 256 if (value == null) { 257 nameValuePairs.remove(name); 258 return this; 259 } 260 if (value instanceof Number) { 261 // deviate from the original by checking all Numbers, not just floats & doubles 262 JSON.checkDouble(((Number) value).doubleValue()); 263 } 264 nameValuePairs.put(checkName(name), value); 265 return this; 266 } 267 268 /** 269 * Equivalent to {@code put(name, value)} when both parameters are non-null; 270 * does nothing otherwise. 271 */ putOpt(String name, Object value)272 public JSONObject putOpt(String name, Object value) throws JSONException { 273 if (name == null || value == null) { 274 return this; 275 } 276 return put(name, value); 277 } 278 279 /** 280 * Appends {@code value} to the array already mapped to {@code name}. If 281 * this object has no mapping for {@code name}, this inserts a new mapping. 282 * If the mapping exists but its value is not an array, the existing 283 * and new values are inserted in order into a new array which is itself 284 * mapped to {@code name}. In aggregate, this allows values to be added to a 285 * mapping one at a time. 286 * 287 * <p> Note that {@code append(String, Object)} provides better semantics. 288 * In particular, the mapping for {@code name} will <b>always</b> be a 289 * {@link JSONArray}. Using {@code accumulate} will result in either a 290 * {@link JSONArray} or a mapping whose type is the type of {@code value} 291 * depending on the number of calls to it. 292 * 293 * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, 294 * Integer, Long, Double, {@link #NULL} or null. May not be {@link 295 * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. 296 */ 297 // TODO: Change {@code append) to {@link #append} when append is 298 // unhidden. accumulate(String name, Object value)299 public JSONObject accumulate(String name, Object value) throws JSONException { 300 Object current = nameValuePairs.get(checkName(name)); 301 if (current == null) { 302 return put(name, value); 303 } 304 305 if (current instanceof JSONArray) { 306 JSONArray array = (JSONArray) current; 307 array.checkedPut(value); 308 } else { 309 JSONArray array = new JSONArray(); 310 array.checkedPut(current); 311 array.checkedPut(value); 312 nameValuePairs.put(name, array); 313 } 314 return this; 315 } 316 317 /** 318 * Appends values to the array mapped to {@code name}. A new {@link JSONArray} 319 * mapping for {@code name} will be inserted if no mapping exists. If the existing 320 * mapping for {@code name} is not a {@link JSONArray}, a {@link JSONException} 321 * will be thrown. 322 * 323 * @throws JSONException if {@code name} is {@code null} or if the mapping for 324 * {@code name} is non-null and is not a {@link JSONArray}. 325 * 326 * @hide 327 */ append(String name, Object value)328 public JSONObject append(String name, Object value) throws JSONException { 329 Object current = nameValuePairs.get(checkName(name)); 330 331 final JSONArray array; 332 if (current instanceof JSONArray) { 333 array = (JSONArray) current; 334 } else if (current == null) { 335 JSONArray newArray = new JSONArray(); 336 nameValuePairs.put(name, newArray); 337 array = newArray; 338 } else { 339 throw new JSONException("Key " + name + " is not a JSONArray"); 340 } 341 342 array.checkedPut(value); 343 344 return this; 345 } 346 checkName(String name)347 String checkName(String name) throws JSONException { 348 if (name == null) { 349 throw new JSONException("Names must be non-null"); 350 } 351 return name; 352 } 353 354 /** 355 * Removes the named mapping if it exists; does nothing otherwise. 356 * 357 * @return the value previously mapped by {@code name}, or null if there was 358 * no such mapping. 359 */ remove(String name)360 public Object remove(String name) { 361 return nameValuePairs.remove(name); 362 } 363 364 /** 365 * Returns true if this object has no mapping for {@code name} or if it has 366 * a mapping whose value is {@link #NULL}. 367 */ isNull(String name)368 public boolean isNull(String name) { 369 Object value = nameValuePairs.get(name); 370 return value == null || value == NULL; 371 } 372 373 /** 374 * Returns true if this object has a mapping for {@code name}. The mapping 375 * may be {@link #NULL}. 376 */ has(String name)377 public boolean has(String name) { 378 return nameValuePairs.containsKey(name); 379 } 380 381 /** 382 * Returns the value mapped by {@code name}, or throws if no such mapping exists. 383 * 384 * @throws JSONException if no such mapping exists. 385 */ get(String name)386 public Object get(String name) throws JSONException { 387 Object result = nameValuePairs.get(name); 388 if (result == null) { 389 throw new JSONException("No value for " + name); 390 } 391 return result; 392 } 393 394 /** 395 * Returns the value mapped by {@code name}, or null if no such mapping 396 * exists. 397 */ opt(String name)398 public Object opt(String name) { 399 return nameValuePairs.get(name); 400 } 401 402 /** 403 * Returns the value mapped by {@code name} if it exists and is a boolean or 404 * can be coerced to a boolean, or throws otherwise. 405 * 406 * @throws JSONException if the mapping doesn't exist or cannot be coerced 407 * to a boolean. 408 */ getBoolean(String name)409 public boolean getBoolean(String name) throws JSONException { 410 Object object = get(name); 411 Boolean result = JSON.toBoolean(object); 412 if (result == null) { 413 throw JSON.typeMismatch(name, object, "boolean"); 414 } 415 return result; 416 } 417 418 /** 419 * Returns the value mapped by {@code name} if it exists and is a boolean or 420 * can be coerced to a boolean, or false otherwise. 421 */ optBoolean(String name)422 public boolean optBoolean(String name) { 423 return optBoolean(name, false); 424 } 425 426 /** 427 * Returns the value mapped by {@code name} if it exists and is a boolean or 428 * can be coerced to a boolean, or {@code fallback} otherwise. 429 */ optBoolean(String name, boolean fallback)430 public boolean optBoolean(String name, boolean fallback) { 431 Object object = opt(name); 432 Boolean result = JSON.toBoolean(object); 433 return result != null ? result : fallback; 434 } 435 436 /** 437 * Returns the value mapped by {@code name} if it exists and is a double or 438 * can be coerced to a double, or throws otherwise. 439 * 440 * @throws JSONException if the mapping doesn't exist or cannot be coerced 441 * to a double. 442 */ getDouble(String name)443 public double getDouble(String name) throws JSONException { 444 Object object = get(name); 445 Double result = JSON.toDouble(object); 446 if (result == null) { 447 throw JSON.typeMismatch(name, object, "double"); 448 } 449 return result; 450 } 451 452 /** 453 * Returns the value mapped by {@code name} if it exists and is a double or 454 * can be coerced to a double, or {@code NaN} otherwise. 455 */ optDouble(String name)456 public double optDouble(String name) { 457 return optDouble(name, Double.NaN); 458 } 459 460 /** 461 * Returns the value mapped by {@code name} if it exists and is a double or 462 * can be coerced to a double, or {@code fallback} otherwise. 463 */ optDouble(String name, double fallback)464 public double optDouble(String name, double fallback) { 465 Object object = opt(name); 466 Double result = JSON.toDouble(object); 467 return result != null ? result : fallback; 468 } 469 470 /** 471 * Returns the value mapped by {@code name} if it exists and is an int or 472 * can be coerced to an int, or throws otherwise. 473 * 474 * @throws JSONException if the mapping doesn't exist or cannot be coerced 475 * to an int. 476 */ getInt(String name)477 public int getInt(String name) throws JSONException { 478 Object object = get(name); 479 Integer result = JSON.toInteger(object); 480 if (result == null) { 481 throw JSON.typeMismatch(name, object, "int"); 482 } 483 return result; 484 } 485 486 /** 487 * Returns the value mapped by {@code name} if it exists and is an int or 488 * can be coerced to an int, or 0 otherwise. 489 */ optInt(String name)490 public int optInt(String name) { 491 return optInt(name, 0); 492 } 493 494 /** 495 * Returns the value mapped by {@code name} if it exists and is an int or 496 * can be coerced to an int, or {@code fallback} otherwise. 497 */ optInt(String name, int fallback)498 public int optInt(String name, int fallback) { 499 Object object = opt(name); 500 Integer result = JSON.toInteger(object); 501 return result != null ? result : fallback; 502 } 503 504 /** 505 * Returns the value mapped by {@code name} if it exists and is a long or 506 * can be coerced to a long, or throws otherwise. 507 * Note that JSON represents numbers as doubles, 508 * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON. 509 * 510 * @throws JSONException if the mapping doesn't exist or cannot be coerced 511 * to a long. 512 */ getLong(String name)513 public long getLong(String name) throws JSONException { 514 Object object = get(name); 515 Long result = JSON.toLong(object); 516 if (result == null) { 517 throw JSON.typeMismatch(name, object, "long"); 518 } 519 return result; 520 } 521 522 /** 523 * Returns the value mapped by {@code name} if it exists and is a long or 524 * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles, 525 * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON. 526 */ optLong(String name)527 public long optLong(String name) { 528 return optLong(name, 0L); 529 } 530 531 /** 532 * Returns the value mapped by {@code name} if it exists and is a long or 533 * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents 534 * numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer 535 * numbers via JSON. 536 */ optLong(String name, long fallback)537 public long optLong(String name, long fallback) { 538 Object object = opt(name); 539 Long result = JSON.toLong(object); 540 return result != null ? result : fallback; 541 } 542 543 /** 544 * Returns the value mapped by {@code name} if it exists, coercing it if 545 * necessary, or throws if no such mapping exists. 546 * 547 * @throws JSONException if no such mapping exists. 548 */ getString(String name)549 public String getString(String name) throws JSONException { 550 Object object = get(name); 551 String result = JSON.toString(object); 552 if (result == null) { 553 throw JSON.typeMismatch(name, object, "String"); 554 } 555 return result; 556 } 557 558 /** 559 * Returns the value mapped by {@code name} if it exists, coercing it if 560 * necessary, or the empty string if no such mapping exists. 561 */ optString(String name)562 public String optString(String name) { 563 return optString(name, ""); 564 } 565 566 /** 567 * Returns the value mapped by {@code name} if it exists, coercing it if 568 * necessary, or {@code fallback} if no such mapping exists. 569 */ optString(String name, String fallback)570 public String optString(String name, String fallback) { 571 Object object = opt(name); 572 String result = JSON.toString(object); 573 return result != null ? result : fallback; 574 } 575 576 /** 577 * Returns the value mapped by {@code name} if it exists and is a {@code 578 * JSONArray}, or throws otherwise. 579 * 580 * @throws JSONException if the mapping doesn't exist or is not a {@code 581 * JSONArray}. 582 */ getJSONArray(String name)583 public JSONArray getJSONArray(String name) throws JSONException { 584 Object object = get(name); 585 if (object instanceof JSONArray) { 586 return (JSONArray) object; 587 } else { 588 throw JSON.typeMismatch(name, object, "JSONArray"); 589 } 590 } 591 592 /** 593 * Returns the value mapped by {@code name} if it exists and is a {@code 594 * JSONArray}, or null otherwise. 595 */ optJSONArray(String name)596 public JSONArray optJSONArray(String name) { 597 Object object = opt(name); 598 return object instanceof JSONArray ? (JSONArray) object : null; 599 } 600 601 /** 602 * Returns the value mapped by {@code name} if it exists and is a {@code 603 * JSONObject}, or throws otherwise. 604 * 605 * @throws JSONException if the mapping doesn't exist or is not a {@code 606 * JSONObject}. 607 */ getJSONObject(String name)608 public JSONObject getJSONObject(String name) throws JSONException { 609 Object object = get(name); 610 if (object instanceof JSONObject) { 611 return (JSONObject) object; 612 } else { 613 throw JSON.typeMismatch(name, object, "JSONObject"); 614 } 615 } 616 617 /** 618 * Returns the value mapped by {@code name} if it exists and is a {@code 619 * JSONObject}, or null otherwise. 620 */ optJSONObject(String name)621 public JSONObject optJSONObject(String name) { 622 Object object = opt(name); 623 return object instanceof JSONObject ? (JSONObject) object : null; 624 } 625 626 /** 627 * Returns an array with the values corresponding to {@code names}. The 628 * array contains null for names that aren't mapped. This method returns 629 * null if {@code names} is either null or empty. 630 */ toJSONArray(JSONArray names)631 public JSONArray toJSONArray(JSONArray names) throws JSONException { 632 JSONArray result = new JSONArray(); 633 if (names == null) { 634 return null; 635 } 636 int length = names.length(); 637 if (length == 0) { 638 return null; 639 } 640 for (int i = 0; i < length; i++) { 641 String name = JSON.toString(names.opt(i)); 642 result.put(opt(name)); 643 } 644 return result; 645 } 646 647 /** 648 * Returns an iterator of the {@code String} names in this object. The 649 * returned iterator supports {@link Iterator#remove() remove}, which will 650 * remove the corresponding mapping from this object. If this object is 651 * modified after the iterator is returned, the iterator's behavior is 652 * undefined. The order of the keys is undefined. 653 */ keys()654 public Iterator<String> keys() { 655 return nameValuePairs.keySet().iterator(); 656 } 657 658 /** 659 * Returns the set of {@code String} names in this object. The returned set 660 * is a view of the keys in this object. {@link Set#remove(Object)} will remove 661 * the corresponding mapping from this object and set iterator behaviour 662 * is undefined if this object is modified after it is returned. 663 * 664 * See {@link #keys()}. 665 * 666 * @hide. 667 */ keySet()668 public Set<String> keySet() { 669 return nameValuePairs.keySet(); 670 } 671 672 /** 673 * Returns an array containing the string names in this object. This method 674 * returns null if this object contains no mappings. 675 */ names()676 public JSONArray names() { 677 return nameValuePairs.isEmpty() 678 ? null 679 : new JSONArray(new ArrayList<String>(nameValuePairs.keySet())); 680 } 681 682 /** 683 * Encodes this object as a compact JSON string, such as: 684 * <pre>{"query":"Pizza","locations":[94043,90210]}</pre> 685 */ toString()686 @Override public String toString() { 687 try { 688 JSONStringer stringer = new JSONStringer(); 689 writeTo(stringer); 690 return stringer.toString(); 691 } catch (JSONException e) { 692 return null; 693 } 694 } 695 696 /** 697 * Encodes this object as a human readable JSON string for debugging, such 698 * as: 699 * <pre> 700 * { 701 * "query": "Pizza", 702 * "locations": [ 703 * 94043, 704 * 90210 705 * ] 706 * }</pre> 707 * 708 * @param indentSpaces the number of spaces to indent for each level of 709 * nesting. 710 */ toString(int indentSpaces)711 public String toString(int indentSpaces) throws JSONException { 712 JSONStringer stringer = new JSONStringer(indentSpaces); 713 writeTo(stringer); 714 return stringer.toString(); 715 } 716 writeTo(JSONStringer stringer)717 void writeTo(JSONStringer stringer) throws JSONException { 718 stringer.object(); 719 for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) { 720 stringer.key(entry.getKey()).value(entry.getValue()); 721 } 722 stringer.endObject(); 723 } 724 725 /** 726 * Encodes the number as a JSON string. 727 * 728 * @param number a finite value. May not be {@link Double#isNaN() NaNs} or 729 * {@link Double#isInfinite() infinities}. 730 */ numberToString(Number number)731 public static String numberToString(Number number) throws JSONException { 732 if (number == null) { 733 throw new JSONException("Number must be non-null"); 734 } 735 736 double doubleValue = number.doubleValue(); 737 JSON.checkDouble(doubleValue); 738 739 // the original returns "-0" instead of "-0.0" for negative zero 740 if (number.equals(NEGATIVE_ZERO)) { 741 return "-0"; 742 } 743 744 long longValue = number.longValue(); 745 if (doubleValue == (double) longValue) { 746 return Long.toString(longValue); 747 } 748 749 return number.toString(); 750 } 751 752 /** 753 * Encodes {@code data} as a JSON string. This applies quotes and any 754 * necessary character escaping. 755 * 756 * @param data the string to encode. Null will be interpreted as an empty 757 * string. 758 */ quote(String data)759 public static String quote(String data) { 760 if (data == null) { 761 return "\"\""; 762 } 763 try { 764 JSONStringer stringer = new JSONStringer(); 765 stringer.open(JSONStringer.Scope.NULL, ""); 766 stringer.value(data); 767 stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); 768 return stringer.toString(); 769 } catch (JSONException e) { 770 throw new AssertionError(); 771 } 772 } 773 774 /** 775 * Wraps the given object if necessary. 776 * 777 * <p>If the object is null or , returns {@link #NULL}. 778 * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary. 779 * If the object is {@code NULL}, no wrapping is necessary. 780 * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}. 781 * If the object is a {@code Map}, returns an equivalent {@code JSONObject}. 782 * If the object is a primitive wrapper type or {@code String}, returns the object. 783 * Otherwise if the object is from a {@code java} package, returns the result of {@code toString}. 784 * If wrapping fails, returns null. 785 */ wrap(Object o)786 public static Object wrap(Object o) { 787 if (o == null) { 788 return NULL; 789 } 790 if (o instanceof JSONArray || o instanceof JSONObject) { 791 return o; 792 } 793 if (o.equals(NULL)) { 794 return o; 795 } 796 try { 797 if (o instanceof Collection) { 798 return new JSONArray((Collection) o); 799 } else if (o.getClass().isArray()) { 800 return new JSONArray(o); 801 } 802 if (o instanceof Map) { 803 return new JSONObject((Map) o); 804 } 805 if (o instanceof Boolean || 806 o instanceof Byte || 807 o instanceof Character || 808 o instanceof Double || 809 o instanceof Float || 810 o instanceof Integer || 811 o instanceof Long || 812 o instanceof Short || 813 o instanceof String) { 814 return o; 815 } 816 if (o.getClass().getPackage().getName().startsWith("java.")) { 817 return o.toString(); 818 } 819 } catch (Exception ignored) { 820 } 821 return null; 822 } 823 } 824