| /* |
| * Copyright (c) 2005-2010 The Android Open Source Project |
| * Copyright (c) 2015-2017 BSI Business Systems Integration AG |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * Contributors: |
| * The Android Open Source Project - initial implementation |
| * BSI Business Systems Integration AG - changes and improvements |
| */ |
| package org.json; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import org.json.JSONStringer.Scope; |
| |
| // Note: this class was written without inspecting the non-free org.json sourcecode. |
| |
| /* |
| * Changes to the original code: |
| * ----------------------------- |
| * - Applied Scout code formatting rules |
| * - Changed getString()/optString() to return null for JSON values "null" instead of the string "null". |
| * - Replaced task markers from the original source code with 'TO.DO'. |
| * - Suppress sonar warnings where necessary |
| * |
| * Copyright (c) 2015 BSI Business Systems Integration AG. |
| */ |
| |
| /** |
| * A modifiable set of name/value mappings. Names are unique, non-null strings. Values may be any mix of |
| * {@link JSONObject JSONObjects}, {@link JSONArray JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or |
| * {@link #NULL}. Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link Double#isInfinite() infinities}, |
| * or of any type not listed here. |
| * <p> |
| * This class can coerce values to another type when requested. |
| * <ul> |
| * <li>When the requested type is a boolean, strings will be coerced using a case-insensitive comparison to "true" and |
| * "false". |
| * <li>When the requested type is a double, other {@link Number} types will be coerced using {@link Number#doubleValue() |
| * doubleValue}. Strings that can be coerced using {@link Double#valueOf(String)} will be. |
| * <li>When the requested type is an int, other {@link Number} types will be coerced using {@link Number#intValue() |
| * intValue}. Strings that can be coerced using {@link Double#valueOf(String)} will be, and then cast to int. |
| * <li><a name="lossy">When the requested type is a long, other {@link Number} types will be coerced using |
| * {@link Number#longValue() longValue}. Strings that can be coerced using {@link Double#valueOf(String)} will be, and |
| * then cast to long. This two-step conversion is lossy for very large values. For example, the string |
| * "9223372036854775806" yields the long 9223372036854775807.</a> |
| * <li>When the requested type is a String, other non-null values will be coerced using {@link String#valueOf(Object)}. |
| * Although null cannot be coerced, the sentinel value {@link JSONObject#NULL} is coerced to the string "null". |
| * </ul> |
| * <p> |
| * This class can look up both mandatory and optional values: |
| * <ul> |
| * <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This fails with a {@code JSONException} if the |
| * requested name has no value or if the value cannot be coerced to the requested type. |
| * <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This returns a system- or user-supplied default |
| * if the requested name has no value or if the value cannot be coerced to the requested type. |
| * </ul> |
| * <p> |
| * <strong>Warning:</strong> this class represents null in two incompatible ways: the standard Java {@code null} |
| * reference, and the sentinel value {@link JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the |
| * named entry from the object but {@code put(name, JSONObject.NULL)} stores an entry whose value is |
| * {@code JSONObject.NULL}. |
| * <p> |
| * Instances of this class are not thread safe. Although this class is nonfinal, it was not designed for inheritance and |
| * should not be subclassed. In particular, self-use by overrideable methods is not specified. See <i>Effective Java</i> |
| * Item 17, "Design and Document or inheritance or else prohibit it" for further information. |
| */ |
| public class JSONObject { |
| |
| private static final Double NEGATIVE_ZERO = -0d; |
| |
| /** |
| * A sentinel value used to explicitly define a name with no value. Unlike {@code null}, names with this value: |
| * <ul> |
| * <li>show up in the {@link #names} array |
| * <li>show up in the {@link #keys} iterator |
| * <li>return {@code true} for {@link #has(String)} |
| * <li>do not throw on {@link #get(String)} |
| * <li>are included in the encoded JSON string. |
| * </ul> |
| * <p> |
| * This value violates the general contract of {@link Object#equals} by returning true when compared to {@code null}. |
| * Its {@link #toString} method returns "null". |
| */ |
| public static final Object NULL = new Object() { |
| @Override |
| @SuppressWarnings({"squid:S1206", "findbugs:EQ_UNUSUAL", "EqualsWhichDoesntCheckParameterClass"}) // hashCode() cannot be implemented for value <code>null</code>. |
| public boolean equals(Object o) { |
| return o == this || o == null; // API specifies this broken equals implementation |
| } |
| |
| // at least make the broken equals(null) consistent with Objects.hashCode(null). |
| @Override |
| public int hashCode() { |
| return Objects.hashCode(null); |
| } |
| |
| @Override |
| public String toString() { |
| return "null"; |
| } |
| }; |
| |
| private final LinkedHashMap<String, Object> m_nameValuePairs; |
| |
| /** |
| * Creates a {@code JSONObject} with no name/value mappings. |
| */ |
| public JSONObject() { |
| m_nameValuePairs = new LinkedHashMap<>(); |
| } |
| |
| /** |
| * Creates a new {@code JSONObject} by copying all name/value mappings from the given map. |
| * |
| * @param copyFrom |
| * a map whose keys are of type {@link String} and whose values are of supported types. |
| * @throws NullPointerException |
| * if any of the map's keys are null. |
| */ |
| @SuppressWarnings("squid:S1695") |
| public JSONObject(Map<?, ?> copyFrom) { |
| this(); |
| for (Entry<?, ?> entry : copyFrom.entrySet()) { |
| /* |
| * Deviate from the original by checking that keys are non-null and |
| * of the proper type. (We still defer validating the values). |
| */ |
| String key = (String) entry.getKey(); |
| if (key == null) { |
| throw new NullPointerException("key == null"); |
| } |
| m_nameValuePairs.put(key, wrap(entry.getValue())); |
| } |
| } |
| |
| /** |
| * Creates a new {@code JSONObject} with name/value mappings from the next object in the tokener. |
| * |
| * @param readFrom |
| * a tokener whose nextValue() method will yield a {@code JSONObject}. |
| * @throws JSONException |
| * if the parse fails or doesn't yield a {@code JSONObject}. |
| */ |
| public JSONObject(JSONTokener readFrom) { |
| /* |
| * Getting the parser to populate this could get tricky. Instead, just |
| * parse to temporary JSONObject and then steal the data from that. |
| */ |
| Object object = readFrom.nextValue(); |
| if (object instanceof JSONObject) { |
| JSONObject jsonObject = (JSONObject) object; |
| this.m_nameValuePairs = jsonObject.m_nameValuePairs; |
| } |
| else { |
| throw JSON.typeMismatch(object, "JSONObject"); |
| } |
| } |
| |
| /** |
| * Creates a new {@code JSONObject} with name/value mappings from the JSON string. |
| * |
| * @param json |
| * a JSON-encoded string containing an object. |
| * @throws JSONException |
| * if the parse fails or doesn't yield a {@code JSONObject}. |
| */ |
| public JSONObject(String json) { |
| this(new JSONTokener(json)); |
| } |
| |
| /** |
| * Creates a new {@code JSONObject} by copying mappings for the listed names from the given object. Names that aren't |
| * present in {@code copyFrom} will be skipped. |
| */ |
| public JSONObject(JSONObject copyFrom, String[] names) { |
| this(); |
| for (String name : names) { |
| Object value = copyFrom.opt(name); |
| if (value != null) { |
| m_nameValuePairs.put(name, value); |
| } |
| } |
| } |
| |
| /** |
| * Returns the number of name/value mappings in this object. |
| */ |
| public int length() { |
| return m_nameValuePairs.size(); |
| } |
| |
| /** |
| * Maps {@code name} to {@code value}, clobbering any existing name/value mapping with the same name. |
| * |
| * @return this object. |
| */ |
| public JSONObject put(String name, boolean value) { |
| m_nameValuePairs.put(checkName(name), value); |
| return this; |
| } |
| |
| /** |
| * Maps {@code name} to {@code value}, clobbering any existing name/value mapping with the same name. |
| * |
| * @param value |
| * a finite value. May not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
| * @return this object. |
| */ |
| public JSONObject put(String name, double value) { |
| m_nameValuePairs.put(checkName(name), JSON.checkDouble(value)); |
| return this; |
| } |
| |
| /** |
| * Maps {@code name} to {@code value}, clobbering any existing name/value mapping with the same name. |
| * |
| * @return this object. |
| */ |
| public JSONObject put(String name, int value) { |
| m_nameValuePairs.put(checkName(name), value); |
| return this; |
| } |
| |
| /** |
| * Maps {@code name} to {@code value}, clobbering any existing name/value mapping with the same name. |
| * |
| * @return this object. |
| */ |
| public JSONObject put(String name, long value) { |
| m_nameValuePairs.put(checkName(name), value); |
| return this; |
| } |
| |
| /** |
| * Maps {@code name} to {@code value}, clobbering any existing name/value mapping with the same name. If the value is |
| * {@code null}, any existing mapping for {@code name} is removed. |
| * |
| * @param value |
| * a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, Double, {@link #NULL}, or |
| * {@code null}. May not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
| * @return this object. |
| */ |
| public JSONObject put(String name, Object value) { |
| if (value == null) { |
| m_nameValuePairs.remove(name); |
| return this; |
| } |
| if (value instanceof Number) { |
| // deviate from the original by checking all Numbers, not just floats & doubles |
| JSON.checkDouble(((Number) value).doubleValue()); |
| } |
| m_nameValuePairs.put(checkName(name), value); |
| return this; |
| } |
| |
| /** |
| * Equivalent to {@code put(name, value)} when both parameters are non-null; does nothing otherwise. |
| */ |
| public JSONObject putOpt(String name, Object value) { |
| if (name == null || value == null) { |
| return this; |
| } |
| return put(name, value); |
| } |
| |
| /** |
| * Appends {@code value} to the array already mapped to {@code name}. If this object has no mapping for {@code name}, |
| * this inserts a new mapping. If the mapping exists but its value is not an array, the existing and new values are |
| * inserted in order into a new array which is itself mapped to {@code name}. In aggregate, this allows values to be |
| * added to a mapping one at a time. |
| * <p> |
| * Note that {@code append(String, Object)} provides better semantics. In particular, the mapping for {@code name} |
| * will <b>always</b> be a {@link JSONArray}. Using {@code accumulate} will result in either a {@link JSONArray} or a |
| * mapping whose type is the type of {@code value} depending on the number of calls to it. |
| * |
| * @param value |
| * a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, Double, {@link #NULL} or null. |
| * May not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
| */ |
| // TO.DO Change {@code append) to {@link #append} when append is |
| // unhidden. |
| public JSONObject accumulate(String name, Object value) { |
| Object current = m_nameValuePairs.get(checkName(name)); |
| if (current == null) { |
| return put(name, value); |
| } |
| |
| if (current instanceof JSONArray) { |
| JSONArray array = (JSONArray) current; |
| array.checkedPut(value); |
| } |
| else { |
| JSONArray array = new JSONArray(); |
| array.checkedPut(current); |
| array.checkedPut(value); |
| m_nameValuePairs.put(name, array); |
| } |
| return this; |
| } |
| |
| /** |
| * Appends values to the array mapped to {@code name}. A new {@link JSONArray} mapping for {@code name} will be |
| * inserted if no mapping exists. If the existing mapping for {@code name} is not a {@link JSONArray}, a |
| * {@link JSONException} will be thrown. |
| * |
| * @throws JSONException |
| * if {@code name} is {@code null} or if the mapping for {@code name} is non-null and is not a |
| * {@link JSONArray}. |
| */ |
| public JSONObject append(String name, Object value) { |
| Object current = m_nameValuePairs.get(checkName(name)); |
| |
| final JSONArray array; |
| if (current instanceof JSONArray) { |
| array = (JSONArray) current; |
| } |
| else if (current == null) { |
| JSONArray newArray = new JSONArray(); |
| m_nameValuePairs.put(name, newArray); |
| array = newArray; |
| } |
| else { |
| throw new JSONException("Key " + name + " is not a JSONArray"); |
| } |
| |
| array.checkedPut(value); |
| |
| return this; |
| } |
| |
| String checkName(String name) { |
| if (name == null) { |
| throw new JSONException("Names must be non-null"); |
| } |
| return name; |
| } |
| |
| /** |
| * Removes the named mapping if it exists; does nothing otherwise. |
| * |
| * @return the value previously mapped by {@code name}, or null if there was no such mapping. |
| */ |
| public Object remove(String name) { |
| return m_nameValuePairs.remove(name); |
| } |
| |
| /** |
| * Returns true if this object has no mapping for {@code name} or if it has a mapping whose value is {@link #NULL}. |
| */ |
| public boolean isNull(String name) { |
| Object value = m_nameValuePairs.get(name); |
| return value == null || value == NULL; |
| } |
| |
| /** |
| * Returns true if this object has a mapping for {@code name}. The mapping may be {@link #NULL}. |
| */ |
| public boolean has(String name) { |
| return m_nameValuePairs.containsKey(name); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name}, or throws if no such mapping exists. |
| * |
| * @throws JSONException |
| * if no such mapping exists. |
| */ |
| public Object get(String name) { |
| Object result = m_nameValuePairs.get(name); |
| if (result == null) { |
| throw new JSONException("No value for " + name); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name}, or null if no such mapping exists. |
| */ |
| public Object opt(String name) { |
| return m_nameValuePairs.get(name); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a boolean or can be coerced to a boolean, or throws |
| * otherwise. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or cannot be coerced to a boolean. |
| */ |
| public boolean getBoolean(String name) { |
| Object object = get(name); |
| Boolean result = JSON.toBoolean(object); |
| if (result == null) { |
| throw JSON.typeMismatch(name, object, "boolean"); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a boolean or can be coerced to a boolean, or false |
| * otherwise. |
| */ |
| public boolean optBoolean(String name) { |
| return optBoolean(name, false); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a boolean or can be coerced to a boolean, or |
| * {@code fallback} otherwise. |
| */ |
| public boolean optBoolean(String name, boolean fallback) { |
| Object object = opt(name); |
| Boolean result = JSON.toBoolean(object); |
| return result != null ? result : fallback; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a double or can be coerced to a double, or throws |
| * otherwise. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or cannot be coerced to a double. |
| */ |
| public double getDouble(String name) { |
| Object object = get(name); |
| Double result = JSON.toDouble(object); |
| if (result == null) { |
| throw JSON.typeMismatch(name, object, "double"); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a double or can be coerced to a double, or {@code NaN} |
| * otherwise. |
| */ |
| public double optDouble(String name) { |
| return optDouble(name, Double.NaN); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a double or can be coerced to a double, or |
| * {@code fallback} otherwise. |
| */ |
| public double optDouble(String name, double fallback) { |
| Object object = opt(name); |
| Double result = JSON.toDouble(object); |
| return result != null ? result : fallback; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is an int or can be coerced to an int, or throws |
| * otherwise. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or cannot be coerced to an int. |
| */ |
| public int getInt(String name) { |
| Object object = get(name); |
| Integer result = JSON.toInteger(object); |
| if (result == null) { |
| throw JSON.typeMismatch(name, object, "int"); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is an int or can be coerced to an int, or 0 otherwise. |
| */ |
| public int optInt(String name) { |
| return optInt(name, 0); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is an int or can be coerced to an int, or |
| * {@code fallback} otherwise. |
| */ |
| public int optInt(String name, int fallback) { |
| Object object = opt(name); |
| Integer result = JSON.toInteger(object); |
| return result != null ? result : fallback; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a long or can be coerced to a long, or throws |
| * otherwise. Note that JSON represents numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to |
| * transfer numbers via JSON. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or cannot be coerced to a long. |
| */ |
| public long getLong(String name) { |
| Object object = get(name); |
| Long result = JSON.toLong(object); |
| if (result == null) { |
| throw JSON.typeMismatch(name, object, "long"); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a long or can be coerced to a long, or 0 otherwise. |
| * Note that JSON represents numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer |
| * numbers via JSON. |
| */ |
| public long optLong(String name) { |
| return optLong(name, 0L); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a long or can be coerced to a long, or |
| * {@code fallback} otherwise. Note that JSON represents numbers as doubles, so this is <a href="#lossy">lossy</a>; |
| * use strings to transfer numbers via JSON. |
| */ |
| public long optLong(String name, long fallback) { |
| Object object = opt(name); |
| Long result = JSON.toLong(object); |
| return result != null ? result : fallback; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists, coercing it if necessary, or throws if no such mapping |
| * exists. |
| * |
| * @throws JSONException |
| * if no such mapping exists. |
| */ |
| public String getString(String name) { |
| Object object = get(name); |
| String result = (NULL.equals(object) ? null : JSON.toString(object)); // BSI |
| if (result == null) { |
| throw JSON.typeMismatch(name, object, "String"); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists, coercing it if necessary, or the empty string if no such |
| * mapping exists. |
| */ |
| public String optString(String name) { |
| return optString(name, ""); |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists, coercing it if necessary, or {@code fallback} if no such |
| * mapping exists. |
| */ |
| public String optString(String name, String fallback) { |
| Object object = opt(name); |
| String result = (NULL.equals(object) ? null : JSON.toString(object)); // BSI |
| return result != null ? result : fallback; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a {@code JSONArray}, or throws otherwise. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or is not a {@code JSONArray}. |
| */ |
| public JSONArray getJSONArray(String name) { |
| Object object = get(name); |
| if (object instanceof JSONArray) { |
| return (JSONArray) object; |
| } |
| else { |
| throw JSON.typeMismatch(name, object, "JSONArray"); |
| } |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a {@code JSONArray}, or null otherwise. |
| */ |
| public JSONArray optJSONArray(String name) { |
| Object object = opt(name); |
| return object instanceof JSONArray ? (JSONArray) object : null; |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a {@code JSONObject}, or throws otherwise. |
| * |
| * @throws JSONException |
| * if the mapping doesn't exist or is not a {@code JSONObject}. |
| */ |
| public JSONObject getJSONObject(String name) { |
| Object object = get(name); |
| if (object instanceof JSONObject) { |
| return (JSONObject) object; |
| } |
| else { |
| throw JSON.typeMismatch(name, object, "JSONObject"); |
| } |
| } |
| |
| /** |
| * Returns the value mapped by {@code name} if it exists and is a {@code JSONObject}, or null otherwise. |
| */ |
| public JSONObject optJSONObject(String name) { |
| Object object = opt(name); |
| return object instanceof JSONObject ? (JSONObject) object : null; |
| } |
| |
| /** |
| * Returns an array with the values corresponding to {@code names}. The array contains null for names that aren't |
| * mapped. This method returns null if {@code names} is either null or empty. |
| */ |
| public JSONArray toJSONArray(JSONArray names) { |
| JSONArray result = new JSONArray(); |
| if (names == null) { |
| return null; |
| } |
| int length = names.length(); |
| if (length == 0) { |
| return null; |
| } |
| for (int i = 0; i < length; i++) { |
| String name = JSON.toString(names.opt(i)); |
| result.put(opt(name)); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns an iterator of the {@code String} names in this object. The returned iterator supports |
| * {@link Iterator#remove() remove}, which will remove the corresponding mapping from this object. If this object is |
| * modified after the iterator is returned, the iterator's behavior is undefined. The order of the keys is undefined. |
| */ |
| public Iterator<String> keys() { |
| return m_nameValuePairs.keySet().iterator(); |
| } |
| |
| /** |
| * Returns the set of {@code String} names in this object. The returned set is a view of the keys in this object. |
| * {@link Set#remove(Object)} will remove the corresponding mapping from this object and set iterator behaviour is |
| * undefined if this object is modified after it is returned. See {@link #keys()}. @hide. |
| */ |
| public Set<String> keySet() { |
| return m_nameValuePairs.keySet(); |
| } |
| |
| /** |
| * Returns an array containing the string names in this object. This method returns null if this object contains no |
| * mappings. |
| */ |
| public JSONArray names() { |
| return m_nameValuePairs.isEmpty() |
| ? null |
| : new JSONArray(new ArrayList<>(m_nameValuePairs.keySet())); |
| } |
| |
| /** |
| * Encodes this object as a compact JSON string, such as: |
| * |
| * <pre> |
| * {"query":"Pizza","locations":[94043,90210]} |
| * </pre> |
| */ |
| @Override |
| @SuppressWarnings({"squid:S1166", "squid:S2225"}) |
| public String toString() { |
| try { |
| JSONStringer stringer = new JSONStringer(); |
| writeTo(stringer); |
| return stringer.toString(); |
| } |
| catch (JSONException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Encodes this object as a human readable JSON string for debugging, such as: |
| * |
| * <pre> |
| * { |
| * "query": "Pizza", |
| * "locations": [ |
| * 94043, |
| * 90210 |
| * ] |
| * } |
| * </pre> |
| * |
| * @param indentSpaces |
| * the number of spaces to indent for each level of nesting. |
| */ |
| public String toString(int indentSpaces) { |
| JSONStringer stringer = new JSONStringer(indentSpaces); |
| writeTo(stringer); |
| return stringer.toString(); |
| } |
| |
| void writeTo(JSONStringer stringer) { |
| stringer.object(); |
| for (Entry<String, Object> entry : m_nameValuePairs.entrySet()) { |
| stringer.key(entry.getKey()).value(entry.getValue()); |
| } |
| stringer.endObject(); |
| } |
| |
| /** |
| * Encodes the number as a JSON string. |
| * |
| * @param number |
| * a finite value. May not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
| */ |
| @SuppressWarnings("squid:S1244") |
| public static String numberToString(Number number) { |
| if (number == null) { |
| throw new JSONException("Number must be non-null"); |
| } |
| |
| double doubleValue = number.doubleValue(); |
| JSON.checkDouble(doubleValue); |
| |
| // the original returns "-0" instead of "-0.0" for negative zero |
| if (number.equals(NEGATIVE_ZERO)) { |
| return "-0"; |
| } |
| |
| long longValue = number.longValue(); |
| if (doubleValue == (double) longValue) { |
| return Long.toString(longValue); |
| } |
| |
| return number.toString(); |
| } |
| |
| /** |
| * Encodes {@code data} as a JSON string. This applies quotes and any necessary character escaping. |
| * |
| * @param data |
| * the string to encode. Null will be interpreted as an empty string. |
| */ |
| public static String quote(String data) { |
| if (data == null) { |
| return "\"\""; |
| } |
| JSONStringer stringer = new JSONStringer(); |
| stringer.open(Scope.NULL, ""); |
| stringer.value(data); |
| stringer.close(Scope.NULL, Scope.NULL, ""); |
| return stringer.toString(); |
| } |
| |
| /** |
| * Wraps the given object if necessary. |
| * <p> |
| * If the object is null, returns {@link #NULL}. If the object is a {@code JSONArray} or {@code JSONObject}, no |
| * wrapping is necessary. If the object is {@code NULL}, no wrapping is necessary. If the object is an array or |
| * {@code Collection}, returns an equivalent {@code JSONArray}. If the object is a {@code Map}, returns an equivalent |
| * {@code JSONObject}. If the object is a primitive wrapper type or {@code String}, returns the object. Otherwise if |
| * the object is from a {@code java} package, returns the result of {@code toString}. If wrapping fails, returns null. |
| */ |
| public static Object wrap(Object o) { |
| if (o == null) { |
| return NULL; |
| } |
| if (o instanceof JSONArray || o instanceof JSONObject) { |
| return o; |
| } |
| if (o.equals(NULL)) { |
| return o; |
| } |
| try { |
| if (o instanceof Collection) { |
| return new JSONArray((Collection<?>) o); |
| } |
| else if (o.getClass().isArray()) { |
| return new JSONArray(o); |
| } |
| if (o instanceof Map) { |
| return new JSONObject((Map<?, ?>) o); |
| } |
| if (o instanceof Boolean || |
| o instanceof Byte || |
| o instanceof Character || |
| o instanceof Double || |
| o instanceof Float || |
| o instanceof Integer || |
| o instanceof Long || |
| o instanceof Short || |
| o instanceof String) { |
| return o; |
| } |
| if (o.getClass().getPackage().getName().startsWith("java.")) { |
| return o.toString(); |
| } |
| } |
| catch (Exception ignored) { // NOSONAR |
| } |
| return null; |
| } |
| } |