1 /*******************************************************************************
\r
2 * ============LICENSE_START==================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * ===========================================================================
\r
7 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * * you may not use this file except in compliance with the License.
\r
9 * * You may obtain a copy of the License at
\r
11 * * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * * Unless required by applicable law or agreed to in writing, software
\r
14 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * * See the License for the specific language governing permissions and
\r
17 * * limitations under the License.
\r
18 * * ============LICENSE_END====================================================
\r
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
26 Copyright (c) 2002 JSON.org
\r
28 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
29 of this software and associated documentation files (the "Software"), to deal
\r
30 in the Software without restriction, including without limitation the rights
\r
31 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
32 copies of the Software, and to permit persons to whom the Software is
\r
33 furnished to do so, subject to the following conditions:
\r
35 The above copyright notice and this permission notice shall be included in all
\r
36 copies or substantial portions of the Software.
\r
38 The Software shall be used for Good, not Evil.
\r
40 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
41 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
42 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
43 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
44 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
45 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
49 import java.io.IOException;
\r
50 import java.io.StringWriter;
\r
51 import java.io.Writer;
\r
52 import java.lang.reflect.Field;
\r
53 import java.lang.reflect.Method;
\r
54 import java.lang.reflect.Modifier;
\r
55 import java.util.Collection;
\r
56 import java.util.Enumeration;
\r
57 import java.util.LinkedHashMap;
\r
58 import java.util.Iterator;
\r
59 import java.util.Locale;
\r
60 import java.util.Map;
\r
61 import java.util.ResourceBundle;
\r
62 import java.util.Set;
\r
65 * A JSONObject is an unordered collection of name/value pairs. Its external
\r
66 * form is a string wrapped in curly braces with colons between the names and
\r
67 * values, and commas between the values and names. The internal form is an
\r
68 * object having <code>get</code> and <code>opt</code> methods for accessing the
\r
69 * values by name, and <code>put</code> methods for adding or replacing values
\r
70 * by name. The values can be any of these types: <code>Boolean</code>,
\r
71 * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
\r
72 * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
\r
73 * constructor can be used to convert an external form JSON text into an
\r
74 * internal form whose values can be retrieved with the <code>get</code> and
\r
75 * <code>opt</code> methods, or to convert values into a JSON text using the
\r
76 * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
\r
77 * returns a value if one can be found, and throws an exception if one cannot be
\r
78 * found. An <code>opt</code> method returns a default value instead of throwing
\r
79 * an exception, and so is useful for obtaining optional values.
\r
81 * The generic <code>get()</code> and <code>opt()</code> methods return an
\r
82 * object, which you can cast or query for type. There are also typed
\r
83 * <code>get</code> and <code>opt</code> methods that do type checking and type
\r
84 * coercion for you. The opt methods differ from the get methods in that they do
\r
85 * not throw. Instead, they return a specified value, such as null.
\r
87 * The <code>put</code> methods add or replace values in an object. For example,
\r
90 * myString = new JSONObject().put("JSON", "Hello, World!").toString();
\r
93 * produces the string <code>{"JSON": "Hello, World"}</code>.
\r
95 * The texts produced by the <code>toString</code> methods strictly conform to
\r
96 * the JSON syntax rules. The constructors are more forgiving in the texts they
\r
99 * <li>An extra <code>,</code> <small>(comma)</small> may appear just
\r
100 * before the closing brace.</li>
\r
101 * <li>Strings may be quoted with <code>'</code> <small>(single
\r
102 * quote)</small>.</li>
\r
103 * <li>Strings do not need to be quoted at all if they do not begin with a quote
\r
104 * or single quote, and if they do not contain leading or trailing spaces, and
\r
105 * if they do not contain any of these characters:
\r
106 * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
\r
107 * if they are not the reserved words <code>true</code>, <code>false</code>, or
\r
108 * <code>null</code>.</li>
\r
109 * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
\r
110 * <code>:</code>.</li>
\r
111 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
\r
112 * well as by <code>,</code> <small>(comma)</small>.</li>
\r
116 * @version 2012-12-01
\r
118 public class LOGJSONObject {
\r
120 * The maximum number of keys in the key pool.
\r
122 private static final int keyPoolSize = 100;
\r
125 * Key pooling is like string interning, but without permanently tying up
\r
126 * memory. To help conserve memory, storage of duplicated key strings in
\r
127 * JSONObjects will be avoided by using a key pool to manage unique key
\r
128 * string objects. This is used by JSONObject.put(string, object).
\r
130 private static Map<String,Object> keyPool = new LinkedHashMap<String,Object>(keyPoolSize);
\r
133 * JSONObject.NULL is equivalent to the value that JavaScript calls null,
\r
134 * whilst Java's null is equivalent to the value that JavaScript calls
\r
137 private static final class Null {
\r
140 * There is only intended to be a single instance of the NULL object,
\r
141 * so the clone method returns itself.
\r
144 protected final Object clone() {
\r
149 * A Null object is equal to the null value and to itself.
\r
150 * @param object An object to test for nullness.
\r
151 * @return true if the object parameter is the JSONObject.NULL object
\r
154 public boolean equals(Object object) {
\r
155 return object == null || object == this;
\r
159 * Get the "null" string value.
\r
160 * @return The string "null".
\r
162 public String toString() {
\r
169 * The map where the JSONObject's properties are kept.
\r
171 private final Map<String,Object> map;
\r
175 * It is sometimes more convenient and less ambiguous to have a
\r
176 * <code>NULL</code> object than to use Java's <code>null</code> value.
\r
177 * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
\r
178 * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
\r
180 public static final Object NULL = new Null();
\r
184 * Construct an empty JSONObject.
\r
186 public LOGJSONObject() {
\r
187 this.map = new LinkedHashMap<String,Object>();
\r
192 * Construct a JSONObject from a subset of another JSONObject.
\r
193 * An array of strings is used to identify the keys that should be copied.
\r
194 * Missing keys are ignored.
\r
195 * @param jo A JSONObject.
\r
196 * @param names An array of strings.
\r
197 * @throws JSONException
\r
198 * @exception JSONException If a value is a non-finite number or if a name is duplicated.
\r
200 public LOGJSONObject(LOGJSONObject jo, String[] names) {
\r
202 for (int i = 0; i < names.length; i += 1) {
\r
204 this.putOnce(names[i], jo.opt(names[i]));
\r
205 } catch (Exception ignore) {
\r
212 * Construct a JSONObject from a JSONTokener.
\r
213 * @param x A JSONTokener object containing the source string.
\r
214 * @throws JSONException If there is a syntax error in the source string
\r
215 * or a duplicated key.
\r
217 public LOGJSONObject(JSONTokener x) throws JSONException {
\r
222 if (x.nextClean() != '{') {
\r
223 throw x.syntaxError("A JSONObject text must begin with '{'");
\r
229 throw x.syntaxError("A JSONObject text must end with '}'");
\r
234 key = x.nextValue().toString();
\r
237 // The key is followed by ':'. We will also tolerate '=' or '=>'.
\r
241 if (x.next() != '>') {
\r
244 } else if (c != ':') {
\r
245 throw x.syntaxError("Expected a ':' after a key");
\r
247 this.putOnce(key, x.nextValue());
\r
249 // Pairs are separated by ','. We will also tolerate ';'.
\r
251 switch (x.nextClean()) {
\r
254 if (x.nextClean() == '}') {
\r
262 throw x.syntaxError("Expected a ',' or '}'");
\r
269 * Construct a JSONObject from a Map.
\r
271 * @param map A map object that can be used to initialize the contents of
\r
273 * @throws JSONException
\r
275 public LOGJSONObject(Map<String,Object> map) {
\r
276 this.map = new LinkedHashMap<String,Object>();
\r
278 Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator();
\r
279 while (i.hasNext()) {
\r
280 Map.Entry<String,Object> e = i.next();
\r
281 Object value = e.getValue();
\r
282 if (value != null) {
\r
283 this.map.put(e.getKey(), wrap(value));
\r
291 * Construct a JSONObject from an Object using bean getters.
\r
292 * It reflects on all of the public methods of the object.
\r
293 * For each of the methods with no parameters and a name starting
\r
294 * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
\r
295 * the method is invoked, and a key and the value returned from the getter method
\r
296 * are put into the new JSONObject.
\r
298 * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
\r
299 * If the second remaining character is not upper case, then the first
\r
300 * character is converted to lower case.
\r
302 * For example, if an object has a method named <code>"getName"</code>, and
\r
303 * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
\r
304 * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
\r
306 * @param bean An object that has getter methods that should be used
\r
307 * to make a JSONObject.
\r
309 public LOGJSONObject(Object bean) {
\r
311 this.populateMap(bean);
\r
316 * Construct a JSONObject from an Object, using reflection to find the
\r
317 * public members. The resulting JSONObject's keys will be the strings
\r
318 * from the names array, and the values will be the field values associated
\r
319 * with those keys in the object. If a key is not found or not visible,
\r
320 * then it will not be copied into the new JSONObject.
\r
321 * @param object An object that has fields that should be used to make a
\r
323 * @param names An array of strings, the names of the fields to be obtained
\r
326 public LOGJSONObject(Object object, String names[]) {
\r
328 Class<? extends Object> c = object.getClass();
\r
329 for (int i = 0; i < names.length; i += 1) {
\r
330 String name = names[i];
\r
332 this.putOpt(name, c.getField(name).get(object));
\r
333 } catch (Exception ignore) {
\r
340 * Construct a JSONObject from a source JSON text string.
\r
341 * This is the most commonly used JSONObject constructor.
\r
342 * @param source A string beginning
\r
343 * with <code>{</code> <small>(left brace)</small> and ending
\r
344 * with <code>}</code> <small>(right brace)</small>.
\r
345 * @exception JSONException If there is a syntax error in the source
\r
346 * string or a duplicated key.
\r
348 public LOGJSONObject(String source) throws JSONException {
\r
349 this(new JSONTokener(source));
\r
354 * Construct a JSONObject from a ResourceBundle.
\r
355 * @param baseName The ResourceBundle base name.
\r
356 * @param locale The Locale to load the ResourceBundle for.
\r
357 * @throws JSONException If any JSONExceptions are detected.
\r
359 public LOGJSONObject(String baseName, Locale locale) throws JSONException {
\r
361 ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
\r
362 Thread.currentThread().getContextClassLoader());
\r
364 // Iterate through the keys in the bundle.
\r
366 Enumeration<?> keys = bundle.getKeys();
\r
367 while (keys.hasMoreElements()) {
\r
368 Object key = keys.nextElement();
\r
369 if (key instanceof String) {
\r
371 // Go through the path, ensuring that there is a nested JSONObject for each
\r
372 // segment except the last. Add the value using the last segment's name into
\r
373 // the deepest nested JSONObject.
\r
375 String[] path = ((String)key).split("\\.");
\r
376 int last = path.length - 1;
\r
377 LOGJSONObject target = this;
\r
378 for (int i = 0; i < last; i += 1) {
\r
379 String segment = path[i];
\r
380 LOGJSONObject nextTarget = target.optJSONObject(segment);
\r
381 if (nextTarget == null) {
\r
382 nextTarget = new LOGJSONObject();
\r
383 target.put(segment, nextTarget);
\r
385 target = nextTarget;
\r
387 target.put(path[last], bundle.getString((String)key));
\r
394 * Accumulate values under a key. It is similar to the put method except
\r
395 * that if there is already an object stored under the key then a
\r
396 * JSONArray is stored under the key to hold all of the accumulated values.
\r
397 * If there is already a JSONArray, then the new value is appended to it.
\r
398 * In contrast, the put method replaces the previous value.
\r
400 * If only one value is accumulated that is not a JSONArray, then the
\r
401 * result will be the same as using put. But if multiple values are
\r
402 * accumulated, then the result will be like append.
\r
403 * @param key A key string.
\r
404 * @param value An object to be accumulated under the key.
\r
406 * @throws JSONException If the value is an invalid number
\r
407 * or if the key is null.
\r
409 public LOGJSONObject accumulate(
\r
412 ) throws JSONException {
\r
413 testValidity(value);
\r
414 Object object = this.opt(key);
\r
415 if (object == null) {
\r
416 this.put(key, value instanceof JSONArray
\r
417 ? new JSONArray().put(value)
\r
419 } else if (object instanceof JSONArray) {
\r
420 ((JSONArray)object).put(value);
\r
422 this.put(key, new JSONArray().put(object).put(value));
\r
429 * Append values to the array under a key. If the key does not exist in the
\r
430 * JSONObject, then the key is put in the JSONObject with its value being a
\r
431 * JSONArray containing the value parameter. If the key was already
\r
432 * associated with a JSONArray, then the value parameter is appended to it.
\r
433 * @param key A key string.
\r
434 * @param value An object to be accumulated under the key.
\r
436 * @throws JSONException If the key is null or if the current value
\r
437 * associated with the key is not a JSONArray.
\r
439 public LOGJSONObject append(String key, Object value) throws JSONException {
\r
440 testValidity(value);
\r
441 Object object = this.opt(key);
\r
442 if (object == null) {
\r
443 this.put(key, new JSONArray().put(value));
\r
444 } else if (object instanceof JSONArray) {
\r
445 this.put(key, ((JSONArray)object).put(value));
\r
447 throw new JSONException("JSONObject[" + key +
\r
448 "] is not a JSONArray.");
\r
455 * Produce a string from a double. The string "null" will be returned if
\r
456 * the number is not finite.
\r
457 * @param d A double.
\r
458 * @return A String.
\r
460 public static String doubleToString(double d) {
\r
461 if (Double.isInfinite(d) || Double.isNaN(d)) {
\r
465 // Shave off trailing zeros and decimal point, if possible.
\r
467 String string = Double.toString(d);
\r
468 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
\r
469 string.indexOf('E') < 0) {
\r
470 while (string.endsWith("0")) {
\r
471 string = string.substring(0, string.length() - 1);
\r
473 if (string.endsWith(".")) {
\r
474 string = string.substring(0, string.length() - 1);
\r
482 * Get the value object associated with a key.
\r
484 * @param key A key string.
\r
485 * @return The object associated with the key.
\r
486 * @throws JSONException if the key is not found.
\r
488 public Object get(String key) throws JSONException {
\r
490 throw new JSONException("Null key.");
\r
492 Object object = this.opt(key);
\r
493 if (object == null) {
\r
494 throw new JSONException("JSONObject[" + quote(key) +
\r
502 * Get the boolean value associated with a key.
\r
504 * @param key A key string.
\r
505 * @return The truth.
\r
506 * @throws JSONException
\r
507 * if the value is not a Boolean or the String "true" or "false".
\r
509 public boolean getBoolean(String key) throws JSONException {
\r
510 Object object = this.get(key);
\r
511 if (object.equals(Boolean.FALSE) ||
\r
512 (object instanceof String &&
\r
513 ((String)object).equalsIgnoreCase("false"))) {
\r
515 } else if (object.equals(Boolean.TRUE) ||
\r
516 (object instanceof String &&
\r
517 ((String)object).equalsIgnoreCase("true"))) {
\r
520 throw new JSONException("JSONObject[" + quote(key) +
\r
521 "] is not a Boolean.");
\r
526 * Get the double value associated with a key.
\r
527 * @param key A key string.
\r
528 * @return The numeric value.
\r
529 * @throws JSONException if the key is not found or
\r
530 * if the value is not a Number object and cannot be converted to a number.
\r
532 public double getDouble(String key) throws JSONException {
\r
533 Object object = this.get(key);
\r
535 return object instanceof Number
\r
536 ? ((Number)object).doubleValue()
\r
537 : Double.parseDouble((String)object);
\r
538 } catch (Exception e) {
\r
539 throw new JSONException("JSONObject[" + quote(key) +
\r
540 "] is not a number.");
\r
546 * Get the int value associated with a key.
\r
548 * @param key A key string.
\r
549 * @return The integer value.
\r
550 * @throws JSONException if the key is not found or if the value cannot
\r
551 * be converted to an integer.
\r
553 public int getInt(String key) throws JSONException {
\r
554 Object object = this.get(key);
\r
556 return object instanceof Number
\r
557 ? ((Number)object).intValue()
\r
558 : Integer.parseInt((String)object);
\r
559 } catch (Exception e) {
\r
560 throw new JSONException("JSONObject[" + quote(key) +
\r
561 "] is not an int.");
\r
567 * Get the JSONArray value associated with a key.
\r
569 * @param key A key string.
\r
570 * @return A JSONArray which is the value.
\r
571 * @throws JSONException if the key is not found or
\r
572 * if the value is not a JSONArray.
\r
574 public JSONArray getJSONArray(String key) throws JSONException {
\r
575 Object object = this.get(key);
\r
576 if (object instanceof JSONArray) {
\r
577 return (JSONArray)object;
\r
579 throw new JSONException("JSONObject[" + quote(key) +
\r
580 "] is not a JSONArray.");
\r
585 * Get the JSONObject value associated with a key.
\r
587 * @param key A key string.
\r
588 * @return A JSONObject which is the value.
\r
589 * @throws JSONException if the key is not found or
\r
590 * if the value is not a JSONObject.
\r
592 public LOGJSONObject getJSONObject(String key) throws JSONException {
\r
593 Object object = this.get(key);
\r
594 if (object instanceof LOGJSONObject) {
\r
595 return (LOGJSONObject)object;
\r
597 throw new JSONException("JSONObject[" + quote(key) +
\r
598 "] is not a JSONObject.");
\r
603 * Get the long value associated with a key.
\r
605 * @param key A key string.
\r
606 * @return The long value.
\r
607 * @throws JSONException if the key is not found or if the value cannot
\r
608 * be converted to a long.
\r
610 public long getLong(String key) throws JSONException {
\r
611 Object object = this.get(key);
\r
613 return object instanceof Number
\r
614 ? ((Number)object).longValue()
\r
615 : Long.parseLong((String)object);
\r
616 } catch (Exception e) {
\r
617 throw new JSONException("JSONObject[" + quote(key) +
\r
618 "] is not a long.");
\r
624 * Get an array of field names from a JSONObject.
\r
626 * @return An array of field names, or null if there are no names.
\r
628 public static String[] getNames(LOGJSONObject jo) {
\r
629 int length = jo.length();
\r
633 Iterator<String> iterator = jo.keys();
\r
634 String[] names = new String[length];
\r
636 while (iterator.hasNext()) {
\r
637 names[i] = iterator.next();
\r
645 * Get an array of field names from an Object.
\r
647 * @return An array of field names, or null if there are no names.
\r
649 public static String[] getNames(Object object) {
\r
650 if (object == null) {
\r
653 Class<? extends Object> klass = object.getClass();
\r
654 Field[] fields = klass.getFields();
\r
655 int length = fields.length;
\r
659 String[] names = new String[length];
\r
660 for (int i = 0; i < length; i += 1) {
\r
661 names[i] = fields[i].getName();
\r
668 * Get the string associated with a key.
\r
670 * @param key A key string.
\r
671 * @return A string which is the value.
\r
672 * @throws JSONException if there is no string value for the key.
\r
674 public String getString(String key) throws JSONException {
\r
675 Object object = this.get(key);
\r
676 if (object instanceof String) {
\r
677 return (String)object;
\r
679 throw new JSONException("JSONObject[" + quote(key) +
\r
680 "] not a string.");
\r
685 * Determine if the JSONObject contains a specific key.
\r
686 * @param key A key string.
\r
687 * @return true if the key exists in the JSONObject.
\r
689 public boolean has(String key) {
\r
690 return this.map.containsKey(key);
\r
695 * Increment a property of a JSONObject. If there is no such property,
\r
696 * create one with a value of 1. If there is such a property, and if
\r
697 * it is an Integer, Long, Double, or Float, then add one to it.
\r
698 * @param key A key string.
\r
700 * @throws JSONException If there is already a property with this name
\r
701 * that is not an Integer, Long, Double, or Float.
\r
703 public LOGJSONObject increment(String key) throws JSONException {
\r
704 Object value = this.opt(key);
\r
705 if (value == null) {
\r
707 } else if (value instanceof Integer) {
\r
708 this.put(key, ((Integer)value).intValue() + 1);
\r
709 } else if (value instanceof Long) {
\r
710 this.put(key, ((Long)value).longValue() + 1);
\r
711 } else if (value instanceof Double) {
\r
712 this.put(key, ((Double)value).doubleValue() + 1);
\r
713 } else if (value instanceof Float) {
\r
714 this.put(key, ((Float)value).floatValue() + 1);
\r
716 throw new JSONException("Unable to increment [" + quote(key) + "].");
\r
723 * Determine if the value associated with the key is null or if there is
\r
725 * @param key A key string.
\r
726 * @return true if there is no value associated with the key or if
\r
727 * the value is the JSONObject.NULL object.
\r
729 public boolean isNull(String key) {
\r
730 return LOGJSONObject.NULL.equals(this.opt(key));
\r
735 * Get an enumeration of the keys of the JSONObject.
\r
737 * @return An iterator of the keys.
\r
739 public Iterator<String> keys() {
\r
740 return this.keySet().iterator();
\r
745 * Get a set of keys of the JSONObject.
\r
747 * @return A keySet.
\r
749 public Set<String> keySet() {
\r
750 return this.map.keySet();
\r
755 * Get the number of keys stored in the JSONObject.
\r
757 * @return The number of keys in the JSONObject.
\r
759 public int length() {
\r
760 return this.map.size();
\r
765 * Produce a JSONArray containing the names of the elements of this
\r
767 * @return A JSONArray containing the key strings, or null if the JSONObject
\r
770 public JSONArray names() {
\r
771 JSONArray ja = new JSONArray();
\r
772 Iterator<String> keys = this.keys();
\r
773 while (keys.hasNext()) {
\r
774 ja.put(keys.next());
\r
776 return ja.length() == 0 ? null : ja;
\r
780 * Produce a string from a Number.
\r
781 * @param number A Number
\r
782 * @return A String.
\r
783 * @throws JSONException If n is a non-finite number.
\r
785 public static String numberToString(Number number)
\r
786 throws JSONException {
\r
787 if (number == null) {
\r
788 throw new JSONException("Null pointer");
\r
790 testValidity(number);
\r
792 // Shave off trailing zeros and decimal point, if possible.
\r
794 String string = number.toString();
\r
795 if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
\r
796 string.indexOf('E') < 0) {
\r
797 while (string.endsWith("0")) {
\r
798 string = string.substring(0, string.length() - 1);
\r
800 if (string.endsWith(".")) {
\r
801 string = string.substring(0, string.length() - 1);
\r
809 * Get an optional value associated with a key.
\r
810 * @param key A key string.
\r
811 * @return An object which is the value, or null if there is no value.
\r
813 public Object opt(String key) {
\r
814 return key == null ? null : this.map.get(key);
\r
819 * Get an optional boolean associated with a key.
\r
820 * It returns false if there is no such key, or if the value is not
\r
821 * Boolean.TRUE or the String "true".
\r
823 * @param key A key string.
\r
824 * @return The truth.
\r
826 public boolean optBoolean(String key) {
\r
827 return this.optBoolean(key, false);
\r
832 * Get an optional boolean associated with a key.
\r
833 * It returns the defaultValue if there is no such key, or if it is not
\r
834 * a Boolean or the String "true" or "false" (case insensitive).
\r
836 * @param key A key string.
\r
837 * @param defaultValue The default.
\r
838 * @return The truth.
\r
840 public boolean optBoolean(String key, boolean defaultValue) {
\r
842 return this.getBoolean(key);
\r
843 } catch (Exception e) {
\r
844 return defaultValue;
\r
850 * Get an optional double associated with a key,
\r
851 * or NaN if there is no such key or if its value is not a number.
\r
852 * If the value is a string, an attempt will be made to evaluate it as
\r
855 * @param key A string which is the key.
\r
856 * @return An object which is the value.
\r
858 public double optDouble(String key) {
\r
859 return this.optDouble(key, Double.NaN);
\r
864 * Get an optional double associated with a key, or the
\r
865 * defaultValue if there is no such key or if its value is not a number.
\r
866 * If the value is a string, an attempt will be made to evaluate it as
\r
869 * @param key A key string.
\r
870 * @param defaultValue The default.
\r
871 * @return An object which is the value.
\r
873 public double optDouble(String key, double defaultValue) {
\r
875 return this.getDouble(key);
\r
876 } catch (Exception e) {
\r
877 return defaultValue;
\r
883 * Get an optional int value associated with a key,
\r
884 * or zero if there is no such key or if the value is not a number.
\r
885 * If the value is a string, an attempt will be made to evaluate it as
\r
888 * @param key A key string.
\r
889 * @return An object which is the value.
\r
891 public int optInt(String key) {
\r
892 return this.optInt(key, 0);
\r
897 * Get an optional int value associated with a key,
\r
898 * or the default if there is no such key or if the value is not a number.
\r
899 * If the value is a string, an attempt will be made to evaluate it as
\r
902 * @param key A key string.
\r
903 * @param defaultValue The default.
\r
904 * @return An object which is the value.
\r
906 public int optInt(String key, int defaultValue) {
\r
908 return this.getInt(key);
\r
909 } catch (Exception e) {
\r
910 return defaultValue;
\r
916 * Get an optional JSONArray associated with a key.
\r
917 * It returns null if there is no such key, or if its value is not a
\r
920 * @param key A key string.
\r
921 * @return A JSONArray which is the value.
\r
923 public JSONArray optJSONArray(String key) {
\r
924 Object o = this.opt(key);
\r
925 return o instanceof JSONArray ? (JSONArray)o : null;
\r
930 * Get an optional JSONObject associated with a key.
\r
931 * It returns null if there is no such key, or if its value is not a
\r
934 * @param key A key string.
\r
935 * @return A JSONObject which is the value.
\r
937 public LOGJSONObject optJSONObject(String key) {
\r
938 Object object = this.opt(key);
\r
939 return object instanceof LOGJSONObject ? (LOGJSONObject)object : null;
\r
944 * Get an optional long value associated with a key,
\r
945 * or zero if there is no such key or if the value is not a number.
\r
946 * If the value is a string, an attempt will be made to evaluate it as
\r
949 * @param key A key string.
\r
950 * @return An object which is the value.
\r
952 public long optLong(String key) {
\r
953 return this.optLong(key, 0);
\r
958 * Get an optional long value associated with a key,
\r
959 * or the default if there is no such key or if the value is not a number.
\r
960 * If the value is a string, an attempt will be made to evaluate it as
\r
963 * @param key A key string.
\r
964 * @param defaultValue The default.
\r
965 * @return An object which is the value.
\r
967 public long optLong(String key, long defaultValue) {
\r
969 return this.getLong(key);
\r
970 } catch (Exception e) {
\r
971 return defaultValue;
\r
977 * Get an optional string associated with a key.
\r
978 * It returns an empty string if there is no such key. If the value is not
\r
979 * a string and is not null, then it is converted to a string.
\r
981 * @param key A key string.
\r
982 * @return A string which is the value.
\r
984 public String optString(String key) {
\r
985 return this.optString(key, "");
\r
990 * Get an optional string associated with a key.
\r
991 * It returns the defaultValue if there is no such key.
\r
993 * @param key A key string.
\r
994 * @param defaultValue The default.
\r
995 * @return A string which is the value.
\r
997 public String optString(String key, String defaultValue) {
\r
998 Object object = this.opt(key);
\r
999 return NULL.equals(object) ? defaultValue : object.toString();
\r
1003 private void populateMap(Object bean) {
\r
1004 Class<? extends Object> klass = bean.getClass();
\r
1006 // If klass is a System class then set includeSuperClass to false.
\r
1008 boolean includeSuperClass = klass.getClassLoader() != null;
\r
1010 Method[] methods = includeSuperClass
\r
1011 ? klass.getMethods()
\r
1012 : klass.getDeclaredMethods();
\r
1013 for (int i = 0; i < methods.length; i += 1) {
\r
1015 Method method = methods[i];
\r
1016 if (Modifier.isPublic(method.getModifiers())) {
\r
1017 String name = method.getName();
\r
1019 if (name.startsWith("get")) {
\r
1020 if ("getClass".equals(name) ||
\r
1021 "getDeclaringClass".equals(name)) {
\r
1024 key = name.substring(3);
\r
1026 } else if (name.startsWith("is")) {
\r
1027 key = name.substring(2);
\r
1029 if (key.length() > 0 &&
\r
1030 Character.isUpperCase(key.charAt(0)) &&
\r
1031 method.getParameterTypes().length == 0) {
\r
1032 if (key.length() == 1) {
\r
1033 key = key.toLowerCase();
\r
1034 } else if (!Character.isUpperCase(key.charAt(1))) {
\r
1035 key = key.substring(0, 1).toLowerCase() +
\r
1039 Object result = method.invoke(bean, (Object[])null);
\r
1040 if (result != null) {
\r
1041 this.map.put(key, wrap(result));
\r
1045 } catch (Exception ignore) {
\r
1052 * Put a key/boolean pair in the JSONObject.
\r
1054 * @param key A key string.
\r
1055 * @param value A boolean which is the value.
\r
1057 * @throws JSONException If the key is null.
\r
1059 public LOGJSONObject put(String key, boolean value) throws JSONException {
\r
1060 this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
\r
1066 * Put a key/value pair in the JSONObject, where the value will be a
\r
1067 * JSONArray which is produced from a Collection.
\r
1068 * @param key A key string.
\r
1069 * @param value A Collection value.
\r
1071 * @throws JSONException
\r
1073 public LOGJSONObject put(String key, Collection<Object> value) throws JSONException {
\r
1074 this.put(key, new JSONArray(value));
\r
1080 * Put a key/double pair in the JSONObject.
\r
1082 * @param key A key string.
\r
1083 * @param value A double which is the value.
\r
1085 * @throws JSONException If the key is null or if the number is invalid.
\r
1087 public LOGJSONObject put(String key, double value) throws JSONException {
\r
1088 this.put(key, new Double(value));
\r
1094 * Put a key/int pair in the JSONObject.
\r
1096 * @param key A key string.
\r
1097 * @param value An int which is the value.
\r
1099 * @throws JSONException If the key is null.
\r
1101 public LOGJSONObject put(String key, int value) throws JSONException {
\r
1102 this.put(key, new Integer(value));
\r
1108 * Put a key/long pair in the JSONObject.
\r
1110 * @param key A key string.
\r
1111 * @param value A long which is the value.
\r
1113 * @throws JSONException If the key is null.
\r
1115 public LOGJSONObject put(String key, long value) throws JSONException {
\r
1116 this.put(key, new Long(value));
\r
1122 * Put a key/value pair in the JSONObject, where the value will be a
\r
1123 * JSONObject which is produced from a Map.
\r
1124 * @param key A key string.
\r
1125 * @param value A Map value.
\r
1127 * @throws JSONException
\r
1129 public LOGJSONObject put(String key, Map<String, Object> value) throws JSONException {
\r
1130 this.put(key, new LOGJSONObject(value));
\r
1136 * Put a key/value pair in the JSONObject. If the value is null,
\r
1137 * then the key will be removed from the JSONObject if it is present.
\r
1138 * @param key A key string.
\r
1139 * @param value An object which is the value. It should be of one of these
\r
1140 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
\r
1141 * or the JSONObject.NULL object.
\r
1143 * @throws JSONException If the value is non-finite number
\r
1144 * or if the key is null.
\r
1146 public LOGJSONObject put(String key, Object value) throws JSONException {
\r
1148 if (key == null) {
\r
1149 throw new JSONException("Null key.");
\r
1151 if (value != null) {
\r
1152 testValidity(value);
\r
1153 pooled = (String)keyPool.get(key);
\r
1154 if (pooled == null) {
\r
1155 if (keyPool.size() >= keyPoolSize) {
\r
1156 keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
\r
1158 keyPool.put(key, key);
\r
1162 this.map.put(key, value);
\r
1171 * Put a key/value pair in the JSONObject, but only if the key and the
\r
1172 * value are both non-null, and only if there is not already a member
\r
1177 * @throws JSONException if the key is a duplicate
\r
1179 public LOGJSONObject putOnce(String key, Object value) throws JSONException {
\r
1180 if (key != null && value != null) {
\r
1181 if (this.opt(key) != null) {
\r
1182 throw new JSONException("Duplicate key \"" + key + "\"");
\r
1184 this.put(key, value);
\r
1191 * Put a key/value pair in the JSONObject, but only if the
\r
1192 * key and the value are both non-null.
\r
1193 * @param key A key string.
\r
1194 * @param value An object which is the value. It should be of one of these
\r
1195 * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
\r
1196 * or the JSONObject.NULL object.
\r
1198 * @throws JSONException If the value is a non-finite number.
\r
1200 public LOGJSONObject putOpt(String key, Object value) throws JSONException {
\r
1201 if (key != null && value != null) {
\r
1202 this.put(key, value);
\r
1209 * Produce a string in double quotes with backslash sequences in all the
\r
1210 * right places. A backslash will be inserted within </, producing <\/,
\r
1211 * allowing JSON text to be delivered in HTML. In JSON text, a string
\r
1212 * cannot contain a control character or an unescaped quote or backslash.
\r
1213 * @param string A String
\r
1214 * @return A String correctly formatted for insertion in a JSON text.
\r
1216 public static String quote(String string) {
\r
1217 StringWriter sw = new StringWriter();
\r
1218 synchronized (sw.getBuffer()) {
\r
1220 return quote(string, sw).toString();
\r
1221 } catch (IOException ignored) {
\r
1222 // will never happen - we are writing to a string writer
\r
1228 public static Writer quote(String string, Writer w) throws IOException {
\r
1229 if (string == null || string.length() == 0) {
\r
1238 int len = string.length();
\r
1241 for (i = 0; i < len; i += 1) {
\r
1243 c = string.charAt(i);
\r
1272 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
\r
1273 || (c >= '\u2000' && c < '\u2100')) {
\r
1275 hhhh = Integer.toHexString(c);
\r
1276 w.write("0000", 0, 4 - hhhh.length());
\r
1288 * Remove a name and its value, if present.
\r
1289 * @param key The name to be removed.
\r
1290 * @return The value that was associated with the name,
\r
1291 * or null if there was no value.
\r
1293 public Object remove(String key) {
\r
1294 return this.map.remove(key);
\r
1298 * Try to convert a string into a number, boolean, or null. If the string
\r
1299 * can't be converted, return the string.
\r
1300 * @param string A String.
\r
1301 * @return A simple JSON value.
\r
1303 public static Object stringToValue(String string) {
\r
1305 if (string.equals("")) {
\r
1308 if (string.equalsIgnoreCase("true")) {
\r
1309 return Boolean.TRUE;
\r
1311 if (string.equalsIgnoreCase("false")) {
\r
1312 return Boolean.FALSE;
\r
1314 if (string.equalsIgnoreCase("null")) {
\r
1315 return LOGJSONObject.NULL;
\r
1319 * If it might be a number, try converting it.
\r
1320 * If a number cannot be produced, then the value will just
\r
1321 * be a string. Note that the plus and implied string
\r
1322 * conventions are non-standard. A JSON parser may accept
\r
1323 * non-JSON forms as long as it accepts all correct JSON forms.
\r
1326 char b = string.charAt(0);
\r
1327 if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
\r
1329 if (string.indexOf('.') > -1 ||
\r
1330 string.indexOf('e') > -1 || string.indexOf('E') > -1) {
\r
1331 d = Double.valueOf(string);
\r
1332 if (!d.isInfinite() && !d.isNaN()) {
\r
1336 Long myLong = new Long(string);
\r
1337 if (myLong.longValue() == myLong.intValue()) {
\r
1338 return new Integer(myLong.intValue());
\r
1343 } catch (Exception ignore) {
\r
1351 * Throw an exception if the object is a NaN or infinite number.
\r
1352 * @param o The object to test.
\r
1353 * @throws JSONException If o is a non-finite number.
\r
1355 public static void testValidity(Object o) throws JSONException {
\r
1357 if (o instanceof Double) {
\r
1358 if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
\r
1359 throw new JSONException(
\r
1360 "JSON does not allow non-finite numbers.");
\r
1362 } else if (o instanceof Float) {
\r
1363 if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
\r
1364 throw new JSONException(
\r
1365 "JSON does not allow non-finite numbers.");
\r
1373 * Produce a JSONArray containing the values of the members of this
\r
1375 * @param names A JSONArray containing a list of key strings. This
\r
1376 * determines the sequence of the values in the result.
\r
1377 * @return A JSONArray of values.
\r
1378 * @throws JSONException If any of the values are non-finite numbers.
\r
1380 public JSONArray toJSONArray(JSONArray names) throws JSONException {
\r
1381 if (names == null || names.length() == 0) {
\r
1384 JSONArray ja = new JSONArray();
\r
1385 for (int i = 0; i < names.length(); i += 1) {
\r
1386 ja.put(this.opt(names.getString(i)));
\r
1392 * Make a JSON text of this JSONObject. For compactness, no whitespace
\r
1393 * is added. If this would not result in a syntactically correct JSON text,
\r
1394 * then null will be returned instead.
\r
1396 * Warning: This method assumes that the data structure is acyclical.
\r
1398 * @return a printable, displayable, portable, transmittable
\r
1399 * representation of the object, beginning
\r
1400 * with <code>{</code> <small>(left brace)</small> and ending
\r
1401 * with <code>}</code> <small>(right brace)</small>.
\r
1403 public String toString() {
\r
1405 return this.toString(0);
\r
1406 } catch (Exception e) {
\r
1413 * Make a prettyprinted JSON text of this JSONObject.
\r
1415 * Warning: This method assumes that the data structure is acyclical.
\r
1416 * @param indentFactor The number of spaces to add to each level of
\r
1418 * @return a printable, displayable, portable, transmittable
\r
1419 * representation of the object, beginning
\r
1420 * with <code>{</code> <small>(left brace)</small> and ending
\r
1421 * with <code>}</code> <small>(right brace)</small>.
\r
1422 * @throws JSONException If the object contains an invalid number.
\r
1424 public String toString(int indentFactor) throws JSONException {
\r
1425 StringWriter w = new StringWriter();
\r
1426 synchronized (w.getBuffer()) {
\r
1427 return this.write(w, indentFactor, 0).toString();
\r
1432 * Make a JSON text of an Object value. If the object has an
\r
1433 * value.toJSONString() method, then that method will be used to produce
\r
1434 * the JSON text. The method is required to produce a strictly
\r
1435 * conforming text. If the object does not contain a toJSONString
\r
1436 * method (which is the most common case), then a text will be
\r
1437 * produced by other means. If the value is an array or Collection,
\r
1438 * then a JSONArray will be made from it and its toJSONString method
\r
1439 * will be called. If the value is a MAP, then a JSONObject will be made
\r
1440 * from it and its toJSONString method will be called. Otherwise, the
\r
1441 * value's toString method will be called, and the result will be quoted.
\r
1444 * Warning: This method assumes that the data structure is acyclical.
\r
1445 * @param value The value to be serialized.
\r
1446 * @return a printable, displayable, transmittable
\r
1447 * representation of the object, beginning
\r
1448 * with <code>{</code> <small>(left brace)</small> and ending
\r
1449 * with <code>}</code> <small>(right brace)</small>.
\r
1450 * @throws JSONException If the value is or contains an invalid number.
\r
1452 @SuppressWarnings("unchecked")
\r
1453 public static String valueToString(Object value) throws JSONException {
\r
1454 if (value == null || value.equals(null)) {
\r
1457 if (value instanceof JSONString) {
\r
1460 object = ((JSONString)value).toJSONString();
\r
1461 } catch (Exception e) {
\r
1462 throw new JSONException(e);
\r
1464 if (object instanceof String) {
\r
1465 return (String)object;
\r
1467 throw new JSONException("Bad value from toJSONString: " + object);
\r
1469 if (value instanceof Number) {
\r
1470 return numberToString((Number) value);
\r
1472 if (value instanceof Boolean || value instanceof LOGJSONObject ||
\r
1473 value instanceof JSONArray) {
\r
1474 return value.toString();
\r
1476 if (value instanceof Map) {
\r
1477 return new LOGJSONObject((Map<String, Object>)value).toString();
\r
1479 if (value instanceof Collection) {
\r
1480 return new JSONArray((Collection<Object>)value).toString();
\r
1482 if (value.getClass().isArray()) {
\r
1483 return new JSONArray(value).toString();
\r
1485 return quote(value.toString());
\r
1489 * Wrap an object, if necessary. If the object is null, return the NULL
\r
1490 * object. If it is an array or collection, wrap it in a JSONArray. If
\r
1491 * it is a map, wrap it in a JSONObject. If it is a standard property
\r
1492 * (Double, String, et al) then it is already wrapped. Otherwise, if it
\r
1493 * comes from one of the java packages, turn it into a string. And if
\r
1494 * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
\r
1495 * then null is returned.
\r
1497 * @param object The object to wrap
\r
1498 * @return The wrapped value
\r
1500 @SuppressWarnings("unchecked")
\r
1501 public static Object wrap(Object object) {
\r
1503 if (object == null) {
\r
1506 if (object instanceof LOGJSONObject || object instanceof JSONArray ||
\r
1507 NULL.equals(object) || object instanceof JSONString ||
\r
1508 object instanceof Byte || object instanceof Character ||
\r
1509 object instanceof Short || object instanceof Integer ||
\r
1510 object instanceof Long || object instanceof Boolean ||
\r
1511 object instanceof Float || object instanceof Double ||
\r
1512 object instanceof String) {
\r
1516 if (object instanceof Collection) {
\r
1517 return new JSONArray((Collection<Object>)object);
\r
1519 if (object.getClass().isArray()) {
\r
1520 return new JSONArray(object);
\r
1522 if (object instanceof Map) {
\r
1523 return new LOGJSONObject((Map<String, Object>)object);
\r
1525 Package objectPackage = object.getClass().getPackage();
\r
1526 String objectPackageName = objectPackage != null
\r
1527 ? objectPackage.getName()
\r
1530 objectPackageName.startsWith("java.") ||
\r
1531 objectPackageName.startsWith("javax.") ||
\r
1532 object.getClass().getClassLoader() == null
\r
1534 return object.toString();
\r
1536 return new LOGJSONObject(object);
\r
1537 } catch(Exception exception) {
\r
1544 * Write the contents of the JSONObject as JSON text to a writer.
\r
1545 * For compactness, no whitespace is added.
\r
1547 * Warning: This method assumes that the data structure is acyclical.
\r
1549 * @return The writer.
\r
1550 * @throws JSONException
\r
1552 public Writer write(Writer writer) throws JSONException {
\r
1553 return this.write(writer, 0, 0);
\r
1557 @SuppressWarnings("unchecked")
\r
1558 static final Writer writeValue(Writer writer, Object value,
\r
1559 int indentFactor, int indent) throws JSONException, IOException {
\r
1560 if (value == null || value.equals(null)) {
\r
1561 writer.write("null");
\r
1562 } else if (value instanceof LOGJSONObject) {
\r
1563 ((LOGJSONObject) value).write(writer, indentFactor, indent);
\r
1564 } else if (value instanceof JSONArray) {
\r
1565 ((JSONArray) value).write(writer, indentFactor, indent);
\r
1566 } else if (value instanceof Map) {
\r
1567 new LOGJSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
\r
1568 } else if (value instanceof Collection) {
\r
1569 new JSONArray((Collection<Object>) value).write(writer, indentFactor,
\r
1571 } else if (value.getClass().isArray()) {
\r
1572 new JSONArray(value).write(writer, indentFactor, indent);
\r
1573 } else if (value instanceof Number) {
\r
1574 writer.write(numberToString((Number) value));
\r
1575 } else if (value instanceof Boolean) {
\r
1576 writer.write(value.toString());
\r
1577 } else if (value instanceof JSONString) {
\r
1580 o = ((JSONString) value).toJSONString();
\r
1581 } catch (Exception e) {
\r
1582 throw new JSONException(e);
\r
1584 writer.write(o != null ? o.toString() : quote(value.toString()));
\r
1586 quote(value.toString(), writer);
\r
1591 static final void indent(Writer writer, int indent) throws IOException {
\r
1592 for (int i = 0; i < indent; i += 1) {
\r
1593 writer.write(' ');
\r
1598 * Write the contents of the JSONObject as JSON text to a writer. For
\r
1599 * compactness, no whitespace is added.
\r
1601 * Warning: This method assumes that the data structure is acyclical.
\r
1603 * @return The writer.
\r
1604 * @throws JSONException
\r
1606 Writer write(Writer writer, int indentFactor, int indent)
\r
1607 throws JSONException {
\r
1609 boolean commanate = false;
\r
1610 final int length = this.length();
\r
1611 Iterator<String> keys = this.keys();
\r
1612 writer.write('{');
\r
1614 if (length == 1) {
\r
1615 Object key = keys.next();
\r
1616 writer.write(quote(key.toString()));
\r
1617 writer.write(':');
\r
1618 if (indentFactor > 0) {
\r
1619 writer.write(' ');
\r
1621 writeValue(writer, this.map.get(key), indentFactor, indent);
\r
1622 } else if (length != 0) {
\r
1623 final int newindent = indent + indentFactor;
\r
1624 while (keys.hasNext()) {
\r
1625 Object key = keys.next();
\r
1627 writer.write(',');
\r
1629 if (indentFactor > 0) {
\r
1630 writer.write('\n');
\r
1632 indent(writer, newindent);
\r
1633 writer.write(quote(key.toString()));
\r
1634 writer.write(':');
\r
1635 if (indentFactor > 0) {
\r
1636 writer.write(' ');
\r
1638 writeValue(writer, this.map.get(key), indentFactor,
\r
1642 if (indentFactor > 0) {
\r
1643 writer.write('\n');
\r
1645 indent(writer, indent);
\r
1647 writer.write('}');
\r
1649 } catch (IOException exception) {
\r
1650 throw new JSONException(exception);
\r