41 Commits

Author SHA1 Message Date
Sean Leary
8e5b516f2b Update README.md 2020-05-17 22:41:27 -05:00
Sean Leary
6b6e8e85d8 Merge pull request #502 from Alanscut/issue-500
update JSONTokener's brief: parse simple json text
2019-12-26 20:56:51 -06:00
Alanscut
6ecbeaa0d2 update JSONTokener's brief: parse simple json text 2019-12-25 11:38:38 +08:00
Sean Leary
3ac647a16f Merge pull request #499 from Alanscut/copyright
Add copyright notice to JSONString, JSONException.
2019-12-23 08:31:01 -06:00
Alanscut
6f06801296 add copyright 2019-12-22 19:17:58 +08:00
Sean Leary
dd7056cb6d Merge pull request #495 from harkue/master
fix typo
2019-11-20 11:20:18 -06:00
harkue
e62d763294 rename hasComma as a better name "needsComma" 2019-11-13 11:46:21 +08:00
harkue
4990c3a180 fix typo 2019-11-12 16:27:24 +08:00
Sean Leary
4b49bc94ce Merge pull request #494 from spaffrath/replace_string_arrays_with_var_args
Replace JSONObject constructor string arrays with var args
2019-11-06 09:31:23 -06:00
Sean Leary
9afa90d9a9 Merge pull request #492 from ggavriilidis/ISSUE-491_clarify_intention_of_JSONArray_toList
Issue-491 - Clarify what the output of the method JSONArray toList is
2019-10-29 22:24:45 -05:00
gavriil
065f9a94bc Issue-491 - modified the comment of JSONArray toList method to clarify what the output of the method is 2019-10-29 21:35:58 +00:00
scott
223e328161 Replace JSONObject constructor string arrays with var args 2019-10-16 21:12:02 -07:00
Sean Leary
044b035847 Merge pull request #486 from johnjaylward/StandardizeExceptionMessages
Standardize Exception Messages
2019-09-24 20:29:17 -05:00
Sean Leary
5b62cf13ff Merge pull request #485 from johnjaylward/FixMLInfiniteLoop
Fix for #484
2019-09-23 17:56:53 -05:00
John J. Aylward
e9c27ab376 standardize exception messages 2019-09-17 11:08:26 -04:00
John J. Aylward
328e7d8944 corrects EOL error when the Meta tag isn't closed properly and we reach
the end of the input.
2019-09-17 10:33:26 -04:00
Sean Leary
2a6af29235 Merge pull request #483 from viniciusls/patch-1
Update README.md to point to latest version
2019-09-07 09:01:19 -05:00
VinĂ­cius Silva
af6d3c63bd Update README.md to point to latest version
Update **Click here if you just want the latest release jar file** on README.md to point to latest version available on Maven Central.
2019-09-06 12:45:41 -03:00
Sean Leary
c3ea0249c9 Merge pull request #481 from johnjaylward/ClarifyParserExceptionsInReadMe
clarifies exception that the parser makes when reading JSON
2019-08-14 11:27:40 -05:00
John J. Aylward
b044b7db4d clarifies exception that the parser makes when reading JSON 2019-08-14 11:47:49 -04:00
Sean Leary
115f8b660b Merge pull request #475 from gaul/static-methods
Make private methods static where possible
2019-08-04 11:18:29 -05:00
Andrew Gaul
f63d21fd13 Make private methods static where possible
This avoids an unneeded object reference.  Found via error-prone.
2019-07-26 15:24:05 -07:00
Sean Leary
c8ae720caf Merge pull request #474 from gaul/stringbuilder
Prefer unsynchronized StringBuilder
2019-07-26 16:07:31 -05:00
Andrew Gaul
4d451468fd Prefer unsynchronized StringBuilder
StringBuffer provides unneeded synchronization.  Found via
error-prone.
2019-07-22 22:26:49 -07:00
Sean Leary
a03a01531a Update README.md 2019-07-22 20:33:02 -05:00
Sean Leary
5b845f28cf Update README.md
New RFC8259, no changes to in-doc references.
2019-05-08 20:27:25 -05:00
Sean Leary
00e0e6c0a2 Merge pull request #467 from meiskalt7/addConfigurationForXsiNilConversionToNull
add configuration for xsi:nil="true" conversion to null
2019-04-22 08:33:16 -05:00
meiskalt7
d5b278539e add configuration for xsi:nil="true" conversion to null 2019-04-09 19:49:32 +07:00
Sean Leary
12bbe8cd9a Merge pull request #452 from johnjaylward/FixEOF
Adds check for EOF
2018-12-13 09:14:10 -06:00
Sean Leary
09dddb826e Merge pull request #412 from johnjaylward/XmlConfig
Initial implementation of XMLParserConfig object for flexible XML Parsing
2018-12-11 07:26:50 -06:00
John J. Aylward
19e9bb6c07 Adds check for EOF 2018-12-10 11:45:53 -05:00
Sean Leary
fea0aca2ab Update README.md 2018-12-08 19:54:06 -06:00
Sean Leary
1a811f1ada Merge pull request #440 from johnjaylward/FixForBigDecimal
Fixes #438 - Corrections to BigDecimal consistency
2018-10-06 14:51:57 -05:00
John J. Aylward
34cfe6df14 removes duplicate code in number getters 2018-10-04 16:02:14 -04:00
John J. Aylward
71c6dd1e34 remove unneeded casts 2018-10-02 15:33:33 -04:00
John J. Aylward
30c1bd16ba fix javadoc 2018-10-02 15:28:53 -04:00
John J. Aylward
bc347d2c19 cleanup of minor warnings 2018-10-02 15:28:24 -04:00
John J. Aylward
a63fa03062 * Fixes opt/getBigDecimal to be consistent
* Performance: Updates JSONWriter to use a regex to decide if writing as a number is best.
2018-10-02 15:28:24 -04:00
Sean Leary
16225efbdd Merge pull request #432 from johnjaylward/patch-2
Update README.md
2018-08-15 08:32:32 -05:00
johnjaylward
b8a3342eb1 Update README.md
update maven search example.
2018-08-15 09:18:14 -04:00
John J. Aylward
ca9df04539 Initial implementation of XMLParserConfig object for more flexible XML parsing 2018-03-21 22:09:46 -04:00
12 changed files with 680 additions and 402 deletions

View File

@@ -55,7 +55,7 @@ public class CDL {
private static String getValue(JSONTokener x) throws JSONException { private static String getValue(JSONTokener x) throws JSONException {
char c; char c;
char q; char q;
StringBuffer sb; StringBuilder sb;
do { do {
c = x.next(); c = x.next();
} while (c == ' ' || c == '\t'); } while (c == ' ' || c == '\t');
@@ -65,7 +65,7 @@ public class CDL {
case '"': case '"':
case '\'': case '\'':
q = c; q = c;
sb = new StringBuffer(); sb = new StringBuilder();
for (;;) { for (;;) {
c = x.next(); c = x.next();
if (c == q) { if (c == q) {
@@ -275,7 +275,7 @@ public class CDL {
if (names == null || names.length() == 0) { if (names == null || names.length() == 0) {
return null; return null;
} }
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < ja.length(); i += 1) { for (int i = 0; i < ja.length(); i += 1) {
JSONObject jo = ja.optJSONObject(i); JSONObject jo = ja.optJSONObject(i);
if (jo != null) { if (jo != null) {

View File

@@ -180,10 +180,16 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Construct a JSONArray from an array * Construct a JSONArray from an array.
*
* @param array
* Array. If the parameter passed is null, or not an array, an
* exception will be thrown.
* *
* @throws JSONException * @throws JSONException
* If not an array or if an array value is non-finite number. * If not an array or if an array value is non-finite number.
* @throws NullPointerException
* Thrown if the array parameter is null.
*/ */
public JSONArray(Object array) throws JSONException { public JSONArray(Object array) throws JSONException {
this(); this();
@@ -243,7 +249,7 @@ public class JSONArray implements Iterable<Object> {
.equalsIgnoreCase("true"))) { .equalsIgnoreCase("true"))) {
return true; return true;
} }
throw new JSONException("JSONArray[" + index + "] is not a boolean."); throw wrongValueFormatException(index, "boolean", null);
} }
/** /**
@@ -257,12 +263,14 @@ public class JSONArray implements Iterable<Object> {
* to a number. * to a number.
*/ */
public double getDouble(int index) throws JSONException { public double getDouble(int index) throws JSONException {
Object object = this.get(index); final Object object = this.get(index);
if(object instanceof Number) {
return ((Number)object).doubleValue();
}
try { try {
return object instanceof Number ? ((Number) object).doubleValue() return Double.parseDouble(object.toString());
: Double.parseDouble((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.", e); throw wrongValueFormatException(index, "double", e);
} }
} }
@@ -277,13 +285,14 @@ public class JSONArray implements Iterable<Object> {
* object and cannot be converted to a number. * object and cannot be converted to a number.
*/ */
public float getFloat(int index) throws JSONException { public float getFloat(int index) throws JSONException {
Object object = this.get(index); final Object object = this.get(index);
if(object instanceof Number) {
return ((Float)object).floatValue();
}
try { try {
return object instanceof Number ? ((Number) object).floatValue() return Float.parseFloat(object.toString());
: Float.parseFloat(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index throw wrongValueFormatException(index, "float", e);
+ "] is not a number.", e);
} }
} }
@@ -305,36 +314,41 @@ public class JSONArray implements Iterable<Object> {
} }
return JSONObject.stringToNumber(object.toString()); return JSONObject.stringToNumber(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.", e); throw wrongValueFormatException(index, "number", e);
} }
} }
/** /**
* Get the enum value associated with an index. * Get the enum value associated with an index.
* *
* @param clazz * @param <E>
* The type of enum to retrieve. * Enum Type
* @param index * @param clazz
* The index must be between 0 and length() - 1. * The type of enum to retrieve.
* @return The enum value at the index location * @param index
* @throws JSONException * The index must be between 0 and length() - 1.
* if the key is not found or if the value cannot be converted * @return The enum value at the index location
* to an enum. * @throws JSONException
*/ * if the key is not found or if the value cannot be converted
* to an enum.
*/
public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException { public <E extends Enum<E>> E getEnum(Class<E> clazz, int index) throws JSONException {
E val = optEnum(clazz, index); E val = optEnum(clazz, index);
if(val==null) { if(val==null) {
// JSONException should really take a throwable argument. // JSONException should really take a throwable argument.
// If it did, I would re-implement this with the Enum.valueOf // If it did, I would re-implement this with the Enum.valueOf
// method and place any thrown exception in the JSONException // method and place any thrown exception in the JSONException
throw new JSONException("JSONArray[" + index + "] is not an enum of type " throw wrongValueFormatException(index, "enum of type "
+ JSONObject.quote(clazz.getSimpleName()) + "."); + JSONObject.quote(clazz.getSimpleName()), null);
} }
return val; return val;
} }
/** /**
* Get the BigDecimal value associated with an index. * Get the BigDecimal value associated with an index. If the value is float
* or double, the the {@link BigDecimal#BigDecimal(double)} constructor
* will be used. See notes on the constructor for conversion issues that
* may arise.
* *
* @param index * @param index
* The index must be between 0 and length() - 1. * The index must be between 0 and length() - 1.
@@ -345,12 +359,11 @@ public class JSONArray implements Iterable<Object> {
*/ */
public BigDecimal getBigDecimal (int index) throws JSONException { public BigDecimal getBigDecimal (int index) throws JSONException {
Object object = this.get(index); Object object = this.get(index);
try { BigDecimal val = JSONObject.objectToBigDecimal(object, null);
return new BigDecimal(object.toString()); if(val == null) {
} catch (Exception e) { throw wrongValueFormatException(index, "BigDecimal", object, null);
throw new JSONException("JSONArray[" + index +
"] could not convert to BigDecimal.", e);
} }
return val;
} }
/** /**
@@ -365,12 +378,11 @@ public class JSONArray implements Iterable<Object> {
*/ */
public BigInteger getBigInteger (int index) throws JSONException { public BigInteger getBigInteger (int index) throws JSONException {
Object object = this.get(index); Object object = this.get(index);
try { BigInteger val = JSONObject.objectToBigInteger(object, null);
return new BigInteger(object.toString()); if(val == null) {
} catch (Exception e) { throw wrongValueFormatException(index, "BigInteger", object, null);
throw new JSONException("JSONArray[" + index +
"] could not convert to BigInteger.", e);
} }
return val;
} }
/** /**
@@ -383,12 +395,14 @@ public class JSONArray implements Iterable<Object> {
* If the key is not found or if the value is not a number. * If the key is not found or if the value is not a number.
*/ */
public int getInt(int index) throws JSONException { public int getInt(int index) throws JSONException {
Object object = this.get(index); final Object object = this.get(index);
if(object instanceof Number) {
return ((Number)object).intValue();
}
try { try {
return object instanceof Number ? ((Number) object).intValue() return Integer.parseInt(object.toString());
: Integer.parseInt((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.", e); throw wrongValueFormatException(index, "int", e);
} }
} }
@@ -407,7 +421,7 @@ public class JSONArray implements Iterable<Object> {
if (object instanceof JSONArray) { if (object instanceof JSONArray) {
return (JSONArray) object; return (JSONArray) object;
} }
throw new JSONException("JSONArray[" + index + "] is not a JSONArray."); throw wrongValueFormatException(index, "JSONArray", null);
} }
/** /**
@@ -425,7 +439,7 @@ public class JSONArray implements Iterable<Object> {
if (object instanceof JSONObject) { if (object instanceof JSONObject) {
return (JSONObject) object; return (JSONObject) object;
} }
throw new JSONException("JSONArray[" + index + "] is not a JSONObject."); throw wrongValueFormatException(index, "JSONObject", null);
} }
/** /**
@@ -439,12 +453,14 @@ public class JSONArray implements Iterable<Object> {
* to a number. * to a number.
*/ */
public long getLong(int index) throws JSONException { public long getLong(int index) throws JSONException {
Object object = this.get(index); final Object object = this.get(index);
if(object instanceof Number) {
return ((Number)object).longValue();
}
try { try {
return object instanceof Number ? ((Number) object).longValue() return Long.parseLong(object.toString());
: Long.parseLong((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.", e); throw wrongValueFormatException(index, "long", e);
} }
} }
@@ -462,7 +478,7 @@ public class JSONArray implements Iterable<Object> {
if (object instanceof String) { if (object instanceof String) {
return (String) object; return (String) object;
} }
throw new JSONException("JSONArray[" + index + "] not a string."); throw wrongValueFormatException(index, "String", null);
} }
/** /**
@@ -489,13 +505,16 @@ public class JSONArray implements Iterable<Object> {
*/ */
public String join(String separator) throws JSONException { public String join(String separator) throws JSONException {
int len = this.length(); int len = this.length();
StringBuilder sb = new StringBuilder(); if (len == 0) {
return "";
}
StringBuilder sb = new StringBuilder(
JSONObject.valueToString(this.myArrayList.get(0)));
for (int i = 0; i < len; i += 1) { for (int i = 1; i < len; i++) {
if (i > 0) { sb.append(separator)
sb.append(separator); .append(JSONObject.valueToString(this.myArrayList.get(i)));
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
} }
return sb.toString(); return sb.toString();
} }
@@ -578,21 +597,15 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public double optDouble(int index, double defaultValue) { public double optDouble(int index, double defaultValue) {
Object val = this.opt(index); final Number val = this.optNumber(index, null);
if (JSONObject.NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ final double doubleValue = val.doubleValue();
return ((Number) val).doubleValue(); // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
} // return defaultValue;
if (val instanceof String) { // }
try { return doubleValue;
return Double.parseDouble((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -620,21 +633,15 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public float optFloat(int index, float defaultValue) { public float optFloat(int index, float defaultValue) {
Object val = this.opt(index); final Number val = this.optNumber(index, null);
if (JSONObject.NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ final float floatValue = val.floatValue();
return ((Number) val).floatValue(); // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
} // return floatValue;
if (val instanceof String) { // }
try { return floatValue;
return Float.parseFloat((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -662,27 +669,18 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public int optInt(int index, int defaultValue) { public int optInt(int index, int defaultValue) {
Object val = this.opt(index); final Number val = this.optNumber(index, null);
if (JSONObject.NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ return val.intValue();
return ((Number) val).intValue();
}
if (val instanceof String) {
try {
return new BigDecimal(val.toString()).intValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E>
* Enum Type
* @param clazz * @param clazz
* The type of enum to retrieve. * The type of enum to retrieve.
* @param index * @param index
@@ -696,6 +694,8 @@ public class JSONArray implements Iterable<Object> {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E>
* Enum Type
* @param clazz * @param clazz
* The type of enum to retrieve. * The type of enum to retrieve.
* @param index * @param index
@@ -725,7 +725,6 @@ public class JSONArray implements Iterable<Object> {
} }
} }
/** /**
* Get the optional BigInteger value associated with an index. The * Get the optional BigInteger value associated with an index. The
* defaultValue is returned if there is no value for the index, or if the * defaultValue is returned if there is no value for the index, or if the
@@ -739,37 +738,16 @@ public class JSONArray implements Iterable<Object> {
*/ */
public BigInteger optBigInteger(int index, BigInteger defaultValue) { public BigInteger optBigInteger(int index, BigInteger defaultValue) {
Object val = this.opt(index); Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) { return JSONObject.objectToBigInteger(val, defaultValue);
return defaultValue;
}
if (val instanceof BigInteger){
return (BigInteger) val;
}
if (val instanceof BigDecimal){
return ((BigDecimal) val).toBigInteger();
}
if (val instanceof Double || val instanceof Float){
return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
}
if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){
return BigInteger.valueOf(((Number) val).longValue());
}
try {
final String valStr = val.toString();
if(JSONObject.isDecimalNotation(valStr)) {
return new BigDecimal(valStr).toBigInteger();
}
return new BigInteger(valStr);
} catch (Exception e) {
return defaultValue;
}
} }
/** /**
* Get the optional BigDecimal value associated with an index. The * Get the optional BigDecimal value associated with an index. The
* defaultValue is returned if there is no value for the index, or if the * defaultValue is returned if there is no value for the index, or if the
* value is not a number and cannot be converted to a number. * value is not a number and cannot be converted to a number. If the value
* is float or double, the the {@link BigDecimal#BigDecimal(double)}
* constructor will be used. See notes on the constructor for conversion
* issues that may arise.
* *
* @param index * @param index
* The index must be between 0 and length() - 1. * The index must be between 0 and length() - 1.
@@ -779,27 +757,7 @@ public class JSONArray implements Iterable<Object> {
*/ */
public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
Object val = this.opt(index); Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) { return JSONObject.objectToBigDecimal(val, defaultValue);
return defaultValue;
}
if (val instanceof BigDecimal){
return (BigDecimal) val;
}
if (val instanceof BigInteger){
return new BigDecimal((BigInteger) val);
}
if (val instanceof Double || val instanceof Float){
return new BigDecimal(((Number) val).doubleValue());
}
if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){
return new BigDecimal(((Number) val).longValue());
}
try {
return new BigDecimal(val.toString());
} catch (Exception e) {
return defaultValue;
}
} }
/** /**
@@ -854,22 +812,11 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public long optLong(int index, long defaultValue) { public long optLong(int index, long defaultValue) {
Object val = this.opt(index); final Number val = this.optNumber(index, null);
if (JSONObject.NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ return val.longValue();
return ((Number) val).longValue();
}
if (val instanceof String) {
try {
return new BigDecimal(val.toString()).longValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -1236,8 +1183,8 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Uses a uaer initialized JSONPointer and tries to * Uses a user initialized JSONPointer and tries to
* match it to an item whithin this JSONArray. For example, given a * match it to an item within this JSONArray. For example, given a
* JSONArray initialized with this document: * JSONArray initialized with this document:
* <pre> * <pre>
* [ * [
@@ -1462,7 +1409,7 @@ public class JSONArray implements Iterable<Object> {
public Writer write(Writer writer, int indentFactor, int indent) public Writer write(Writer writer, int indentFactor, int indent)
throws JSONException { throws JSONException {
try { try {
boolean commanate = false; boolean needsComma = false;
int length = this.length(); int length = this.length();
writer.write('['); writer.write('[');
@@ -1474,23 +1421,23 @@ public class JSONArray implements Iterable<Object> {
throw new JSONException("Unable to write JSONArray value at index: 0", e); throw new JSONException("Unable to write JSONArray value at index: 0", e);
} }
} else if (length != 0) { } else if (length != 0) {
final int newindent = indent + indentFactor; final int newIndent = indent + indentFactor;
for (int i = 0; i < length; i += 1) { for (int i = 0; i < length; i += 1) {
if (commanate) { if (needsComma) {
writer.write(','); writer.write(',');
} }
if (indentFactor > 0) { if (indentFactor > 0) {
writer.write('\n'); writer.write('\n');
} }
JSONObject.indent(writer, newindent); JSONObject.indent(writer, newIndent);
try { try {
JSONObject.writeValue(writer, this.myArrayList.get(i), JSONObject.writeValue(writer, this.myArrayList.get(i),
indentFactor, newindent); indentFactor, newIndent);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("Unable to write JSONArray value at index: " + i, e); throw new JSONException("Unable to write JSONArray value at index: " + i, e);
} }
commanate = true; needsComma = true;
} }
if (indentFactor > 0) { if (indentFactor > 0) {
writer.write('\n'); writer.write('\n');
@@ -1507,7 +1454,7 @@ public class JSONArray implements Iterable<Object> {
/** /**
* Returns a java.util.List containing all of the elements in this array. * Returns a java.util.List containing all of the elements in this array.
* If an element in the array is a JSONArray or JSONObject it will also * If an element in the array is a JSONArray or JSONObject it will also
* be converted. * be converted to a List and a Map respectively.
* <p> * <p>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* *
@@ -1535,7 +1482,40 @@ public class JSONArray implements Iterable<Object> {
* @return true if JSONArray is empty, otherwise false. * @return true if JSONArray is empty, otherwise false.
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return myArrayList.isEmpty(); return this.myArrayList.isEmpty();
}
/**
* Create a new JSONException in a common format for incorrect conversions.
* @param idx index of the item
* @param valueType the type of value being coerced to
* @param cause optional cause of the coercion failure
* @return JSONException that can be thrown.
*/
private static JSONException wrongValueFormatException(
int idx,
String valueType,
Throwable cause) {
return new JSONException(
"JSONArray[" + idx + "] is not a " + valueType + "."
, cause);
}
/**
* Create a new JSONException in a common format for incorrect conversions.
* @param idx index of the item
* @param valueType the type of value being coerced to
* @param cause optional cause of the coercion failure
* @return JSONException that can be thrown.
*/
private static JSONException wrongValueFormatException(
int idx,
String valueType,
Object value,
Throwable cause) {
return new JSONException(
"JSONArray[" + idx + "] is not a " + valueType + " (" + value + ")."
, cause);
} }
} }

View File

@@ -1,5 +1,29 @@
package org.json; package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/** /**
* The JSONException is thrown by the JSON.org classes when things are amiss. * The JSONException is thrown by the JSON.org classes when things are amiss.
* *

View File

@@ -45,6 +45,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
/** /**
* A JSONObject is an unordered collection of name/value pairs. Its external * A JSONObject is an unordered collection of name/value pairs. Its external
@@ -150,6 +151,12 @@ public class JSONObject {
return "null"; return "null";
} }
} }
/**
* Regular Expression Pattern that matches JSON Numbers. This is primarily used for
* output to guarantee that we are always writing valid JSON.
*/
static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?");
/** /**
* The map where the JSONObject's properties are kept. * The map where the JSONObject's properties are kept.
@@ -187,7 +194,7 @@ public class JSONObject {
* @param names * @param names
* An array of strings. * An array of strings.
*/ */
public JSONObject(JSONObject jo, String[] names) { public JSONObject(JSONObject jo, String ... names) {
this(names.length); this(names.length);
for (int i = 0; i < names.length; i += 1) { for (int i = 0; i < names.length; i += 1) {
try { try {
@@ -371,7 +378,7 @@ public class JSONObject {
* An array of strings, the names of the fields to be obtained * An array of strings, the names of the fields to be obtained
* from the object. * from the object.
*/ */
public JSONObject(Object object, String names[]) { public JSONObject(Object object, String ... names) {
this(names.length); this(names.length);
Class<?> c = object.getClass(); Class<?> c = object.getClass();
for (int i = 0; i < names.length; i += 1) { for (int i = 0; i < names.length; i += 1) {
@@ -514,8 +521,7 @@ public class JSONObject {
} else if (object instanceof JSONArray) { } else if (object instanceof JSONArray) {
this.put(key, ((JSONArray) object).put(value)); this.put(key, ((JSONArray) object).put(value));
} else { } else {
throw new JSONException("JSONObject[" + key throw wrongValueFormatException(key, "JSONArray", null, null);
+ "] is not a JSONArray.");
} }
return this; return this;
} }
@@ -569,26 +575,26 @@ public class JSONObject {
} }
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param clazz * @param <E>
* The type of enum to retrieve. * Enum Type
* @param key * @param clazz
* A key string. * The type of enum to retrieve.
* @return The enum value associated with the key * @param key
* @throws JSONException * A key string.
* if the key is not found or if the value cannot be converted * @return The enum value associated with the key
* to an enum. * @throws JSONException
*/ * if the key is not found or if the value cannot be converted
* to an enum.
*/
public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) throws JSONException { public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) throws JSONException {
E val = optEnum(clazz, key); E val = optEnum(clazz, key);
if(val==null) { if(val==null) {
// JSONException should really take a throwable argument. // JSONException should really take a throwable argument.
// If it did, I would re-implement this with the Enum.valueOf // If it did, I would re-implement this with the Enum.valueOf
// method and place any thrown exception in the JSONException // method and place any thrown exception in the JSONException
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), null);
+ "] is not an enum of type " + quote(clazz.getSimpleName())
+ ".");
} }
return val; return val;
} }
@@ -614,8 +620,7 @@ public class JSONObject {
.equalsIgnoreCase("true"))) { .equalsIgnoreCase("true"))) {
return true; return true;
} }
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "Boolean", null);
+ "] is not a Boolean.");
} }
/** /**
@@ -630,16 +635,18 @@ public class JSONObject {
*/ */
public BigInteger getBigInteger(String key) throws JSONException { public BigInteger getBigInteger(String key) throws JSONException {
Object object = this.get(key); Object object = this.get(key);
try { BigInteger ret = objectToBigInteger(object, null);
return new BigInteger(object.toString()); if (ret != null) {
} catch (Exception e) { return ret;
throw new JSONException("JSONObject[" + quote(key)
+ "] could not be converted to BigInteger.", e);
} }
throw wrongValueFormatException(key, "BigInteger", object, null);
} }
/** /**
* Get the BigDecimal value associated with a key. * Get the BigDecimal value associated with a key. If the value is float or
* double, the the {@link BigDecimal#BigDecimal(double)} constructor will
* be used. See notes on the constructor for conversion issues that may
* arise.
* *
* @param key * @param key
* A key string. * A key string.
@@ -650,15 +657,11 @@ public class JSONObject {
*/ */
public BigDecimal getBigDecimal(String key) throws JSONException { public BigDecimal getBigDecimal(String key) throws JSONException {
Object object = this.get(key); Object object = this.get(key);
if (object instanceof BigDecimal) { BigDecimal ret = objectToBigDecimal(object, null);
return (BigDecimal)object; if (ret != null) {
} return ret;
try {
return new BigDecimal(object.toString());
} catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key)
+ "] could not be converted to BigDecimal.", e);
} }
throw wrongValueFormatException(key, "BigDecimal", object, null);
} }
/** /**
@@ -672,13 +675,14 @@ public class JSONObject {
* object and cannot be converted to a number. * object and cannot be converted to a number.
*/ */
public double getDouble(String key) throws JSONException { public double getDouble(String key) throws JSONException {
Object object = this.get(key); final Object object = this.get(key);
if(object instanceof Number) {
return ((Number)object).doubleValue();
}
try { try {
return object instanceof Number ? ((Number) object).doubleValue() return Double.parseDouble(object.toString());
: Double.parseDouble(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "double", e);
+ "] is not a number.", e);
} }
} }
@@ -693,13 +697,14 @@ public class JSONObject {
* object and cannot be converted to a number. * object and cannot be converted to a number.
*/ */
public float getFloat(String key) throws JSONException { public float getFloat(String key) throws JSONException {
Object object = this.get(key); final Object object = this.get(key);
if(object instanceof Number) {
return ((Number)object).floatValue();
}
try { try {
return object instanceof Number ? ((Number) object).floatValue() return Float.parseFloat(object.toString());
: Float.parseFloat(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "float", e);
+ "] is not a number.", e);
} }
} }
@@ -721,8 +726,7 @@ public class JSONObject {
} }
return stringToNumber(object.toString()); return stringToNumber(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "number", e);
+ "] is not a number.", e);
} }
} }
@@ -737,13 +741,14 @@ public class JSONObject {
* to an integer. * to an integer.
*/ */
public int getInt(String key) throws JSONException { public int getInt(String key) throws JSONException {
Object object = this.get(key); final Object object = this.get(key);
if(object instanceof Number) {
return ((Number)object).intValue();
}
try { try {
return object instanceof Number ? ((Number) object).intValue() return Integer.parseInt(object.toString());
: Integer.parseInt((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "int", e);
+ "] is not an int.", e);
} }
} }
@@ -761,8 +766,7 @@ public class JSONObject {
if (object instanceof JSONArray) { if (object instanceof JSONArray) {
return (JSONArray) object; return (JSONArray) object;
} }
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "JSONArray", null);
+ "] is not a JSONArray.");
} }
/** /**
@@ -779,8 +783,7 @@ public class JSONObject {
if (object instanceof JSONObject) { if (object instanceof JSONObject) {
return (JSONObject) object; return (JSONObject) object;
} }
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "JSONObject", null);
+ "] is not a JSONObject.");
} }
/** /**
@@ -794,19 +797,22 @@ public class JSONObject {
* to a long. * to a long.
*/ */
public long getLong(String key) throws JSONException { public long getLong(String key) throws JSONException {
Object object = this.get(key); final Object object = this.get(key);
if(object instanceof Number) {
return ((Number)object).longValue();
}
try { try {
return object instanceof Number ? ((Number) object).longValue() return Long.parseLong(object.toString());
: Long.parseLong((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key) throw wrongValueFormatException(key, "long", e);
+ "] is not a long.", e);
} }
} }
/** /**
* Get an array of field names from a JSONObject. * Get an array of field names from a JSONObject.
* *
* @param jo
* JSON object
* @return An array of field names, or null if there are no names. * @return An array of field names, or null if there are no names.
*/ */
public static String[] getNames(JSONObject jo) { public static String[] getNames(JSONObject jo) {
@@ -817,8 +823,10 @@ public class JSONObject {
} }
/** /**
* Get an array of field names from an Object. * Get an array of public field names from an Object.
* *
* @param object
* object to read
* @return An array of field names, or null if there are no names. * @return An array of field names, or null if there are no names.
*/ */
public static String[] getNames(Object object) { public static String[] getNames(Object object) {
@@ -852,7 +860,7 @@ public class JSONObject {
if (object instanceof String) { if (object instanceof String) {
return (String) object; return (String) object;
} }
throw new JSONException("JSONObject[" + quote(key) + "] not a string."); throw wrongValueFormatException(key, "string", null);
} }
/** /**
@@ -868,8 +876,11 @@ public class JSONObject {
/** /**
* Increment a property of a JSONObject. If there is no such property, * Increment a property of a JSONObject. If there is no such property,
* create one with a value of 1. If there is such a property, and if it is * create one with a value of 1 (Integer). If there is such a property, and if it is
* an Integer, Long, Double, or Float, then add one to it. * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it.
* No overflow bounds checking is performed, so callers should initialize the key
* prior to this call with an appropriate type that can handle the maximum expected
* value.
* *
* @param key * @param key
* A key string. * A key string.
@@ -882,18 +893,18 @@ public class JSONObject {
Object value = this.opt(key); Object value = this.opt(key);
if (value == null) { if (value == null) {
this.put(key, 1); this.put(key, 1);
} else if (value instanceof BigInteger) {
this.put(key, ((BigInteger)value).add(BigInteger.ONE));
} else if (value instanceof BigDecimal) {
this.put(key, ((BigDecimal)value).add(BigDecimal.ONE));
} else if (value instanceof Integer) { } else if (value instanceof Integer) {
this.put(key, ((Integer) value).intValue() + 1); this.put(key, ((Integer) value).intValue() + 1);
} else if (value instanceof Long) { } else if (value instanceof Long) {
this.put(key, ((Long) value).longValue() + 1L); this.put(key, ((Long) value).longValue() + 1L);
} else if (value instanceof Double) { } else if (value instanceof BigInteger) {
this.put(key, ((Double) value).doubleValue() + 1.0d); this.put(key, ((BigInteger)value).add(BigInteger.ONE));
} else if (value instanceof Float) { } else if (value instanceof Float) {
this.put(key, ((Float) value).floatValue() + 1.0f); this.put(key, ((Float) value).floatValue() + 1.0f);
} else if (value instanceof Double) {
this.put(key, ((Double) value).doubleValue() + 1.0d);
} else if (value instanceof BigDecimal) {
this.put(key, ((BigDecimal)value).add(BigDecimal.ONE));
} else { } else {
throw new JSONException("Unable to increment [" + quote(key) + "]."); throw new JSONException("Unable to increment [" + quote(key) + "].");
} }
@@ -968,7 +979,7 @@ public class JSONObject {
* @return true if JSONObject is empty, otherwise false. * @return true if JSONObject is empty, otherwise false.
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return map.isEmpty(); return this.map.isEmpty();
} }
/** /**
@@ -1029,6 +1040,8 @@ public class JSONObject {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E>
* Enum Type
* @param clazz * @param clazz
* The type of enum to retrieve. * The type of enum to retrieve.
* @param key * @param key
@@ -1042,6 +1055,8 @@ public class JSONObject {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E>
* Enum Type
* @param clazz * @param clazz
* The type of enum to retrieve. * The type of enum to retrieve.
* @param key * @param key
@@ -1113,7 +1128,10 @@ public class JSONObject {
/** /**
* Get an optional BigDecimal associated with a key, or the defaultValue if * Get an optional BigDecimal associated with a key, or the defaultValue if
* there is no such key or if its value is not a number. If the value is a * there is no such key or if its value is not a number. If the value is a
* string, an attempt will be made to evaluate it as a number. * string, an attempt will be made to evaluate it as a number. If the value
* is float or double, then the {@link BigDecimal#BigDecimal(double)}
* constructor will be used. See notes on the constructor for conversion
* issues that may arise.
* *
* @param key * @param key
* A key string. * A key string.
@@ -1123,6 +1141,16 @@ public class JSONObject {
*/ */
public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
Object val = this.opt(key); Object val = this.opt(key);
return objectToBigDecimal(val, defaultValue);
}
/**
* @param val value to convert
* @param defaultValue default value to return is the conversion doesn't work or is null.
* @return BigDecimal conversion of the original value, or the defaultValue if unable
* to convert.
*/
static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
if (NULL.equals(val)) { if (NULL.equals(val)) {
return defaultValue; return defaultValue;
} }
@@ -1133,6 +1161,10 @@ public class JSONObject {
return new BigDecimal((BigInteger) val); return new BigDecimal((BigInteger) val);
} }
if (val instanceof Double || val instanceof Float){ if (val instanceof Double || val instanceof Float){
final double d = ((Number) val).doubleValue();
if(Double.isNaN(d)) {
return defaultValue;
}
return new BigDecimal(((Number) val).doubleValue()); return new BigDecimal(((Number) val).doubleValue());
} }
if (val instanceof Long || val instanceof Integer if (val instanceof Long || val instanceof Integer
@@ -1160,6 +1192,16 @@ public class JSONObject {
*/ */
public BigInteger optBigInteger(String key, BigInteger defaultValue) { public BigInteger optBigInteger(String key, BigInteger defaultValue) {
Object val = this.opt(key); Object val = this.opt(key);
return objectToBigInteger(val, defaultValue);
}
/**
* @param val value to convert
* @param defaultValue default value to return is the conversion doesn't work or is null.
* @return BigInteger conversion of the original value, or the defaultValue if unable
* to convert.
*/
static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
if (NULL.equals(val)) { if (NULL.equals(val)) {
return defaultValue; return defaultValue;
} }
@@ -1170,7 +1212,11 @@ public class JSONObject {
return ((BigDecimal) val).toBigInteger(); return ((BigDecimal) val).toBigInteger();
} }
if (val instanceof Double || val instanceof Float){ if (val instanceof Double || val instanceof Float){
return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); final double d = ((Number) val).doubleValue();
if(Double.isNaN(d)) {
return defaultValue;
}
return new BigDecimal(d).toBigInteger();
} }
if (val instanceof Long || val instanceof Integer if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){ || val instanceof Short || val instanceof Byte){
@@ -1218,21 +1264,15 @@ public class JSONObject {
* @return An object which is the value. * @return An object which is the value.
*/ */
public double optDouble(String key, double defaultValue) { public double optDouble(String key, double defaultValue) {
Object val = this.opt(key); Number val = this.optNumber(key);
if (NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ final double doubleValue = val.doubleValue();
return ((Number) val).doubleValue(); // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
} // return defaultValue;
if (val instanceof String) { // }
try { return doubleValue;
return Double.parseDouble((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -1260,21 +1300,15 @@ public class JSONObject {
* @return The value. * @return The value.
*/ */
public float optFloat(String key, float defaultValue) { public float optFloat(String key, float defaultValue) {
Object val = this.opt(key); Number val = this.optNumber(key);
if (JSONObject.NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ final float floatValue = val.floatValue();
return ((Number) val).floatValue(); // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
} // return defaultValue;
if (val instanceof String) { // }
try { return floatValue;
return Float.parseFloat((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -1302,22 +1336,11 @@ public class JSONObject {
* @return An object which is the value. * @return An object which is the value.
*/ */
public int optInt(String key, int defaultValue) { public int optInt(String key, int defaultValue) {
Object val = this.opt(key); final Number val = this.optNumber(key, null);
if (NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){ return val.intValue();
return ((Number) val).intValue();
}
if (val instanceof String) {
try {
return new BigDecimal((String) val).intValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -1371,22 +1394,12 @@ public class JSONObject {
* @return An object which is the value. * @return An object which is the value.
*/ */
public long optLong(String key, long defaultValue) { public long optLong(String key, long defaultValue) {
Object val = this.opt(key); final Number val = this.optNumber(key, null);
if (NULL.equals(val)) { if (val == null) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){
return ((Number) val).longValue();
}
if (val instanceof String) { return val.longValue();
try {
return new BigDecimal((String) val).longValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -1424,14 +1437,11 @@ public class JSONObject {
return (Number) val; return (Number) val;
} }
if (val instanceof String) { try {
try { return stringToNumber(val.toString());
return stringToNumber((String) val); } catch (Exception e) {
} catch (Exception e) { return defaultValue;
return defaultValue;
}
} }
return defaultValue;
} }
/** /**
@@ -1512,11 +1522,11 @@ public class JSONObject {
} }
} }
private boolean isValidMethodName(String name) { private static boolean isValidMethodName(String name) {
return !"getClass".equals(name) && !"getDeclaringClass".equals(name); return !"getClass".equals(name) && !"getDeclaringClass".equals(name);
} }
private String getKeyNameFromMethod(Method method) { private static String getKeyNameFromMethod(Method method) {
final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class); final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class);
if (ignoreDepth > 0) { if (ignoreDepth > 0) {
final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class); final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class);
@@ -1823,8 +1833,10 @@ public class JSONObject {
* are both non-null, and only if there is not already a member with that * are both non-null, and only if there is not already a member with that
* name. * name.
* *
* @param key string * @param key
* @param value object * key to insert into
* @param value
* value to insert
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* if the key is a duplicate * if the key is a duplicate
@@ -1935,9 +1947,10 @@ public class JSONObject {
/** /**
* Produce a string in double quotes with backslash sequences in all the * Produce a string in double quotes with backslash sequences in all the
* right places. A backslash will be inserted within </, producing <\/, * right places. A backslash will be inserted within &lt;/, producing
* allowing JSON text to be delivered in HTML. In JSON text, a string cannot * &lt;\/, allowing JSON text to be delivered in HTML. In JSON text, a
* contain a control character or an unescaped quote or backslash. * string cannot contain a control character or an unescaped quote or
* backslash.
* *
* @param string * @param string
* A String * A String
@@ -2128,7 +2141,7 @@ public class JSONObject {
//} //}
//return new BigInteger(val); //return new BigInteger(val);
// BigInteger version: We use a similar bitLenth compare as // BigInteger version: We use a similar bitLength compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold // BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is // only what they need. i.e. Less runtime overhead if the value is
// long lived. Which is the better tradeoff? This is closer to what's // long lived. Which is the better tradeoff? This is closer to what's
@@ -2150,22 +2163,26 @@ public class JSONObject {
* can't be converted, return the string. * can't be converted, return the string.
* *
* @param string * @param string
* A String. * A String. can not be null.
* @return A simple JSON value. * @return A simple JSON value.
* @throws NullPointerException
* Thrown if the string is null.
*/ */
// Changes to this method must be copied to the corresponding method in // Changes to this method must be copied to the corresponding method in
// the XML class to keep full support for Android // the XML class to keep full support for Android
public static Object stringToValue(String string) { public static Object stringToValue(String string) {
if (string.equals("")) { if ("".equals(string)) {
return string; return string;
} }
if (string.equalsIgnoreCase("true")) {
// check JSON key words true/false/null
if ("true".equalsIgnoreCase(string)) {
return Boolean.TRUE; return Boolean.TRUE;
} }
if (string.equalsIgnoreCase("false")) { if ("false".equalsIgnoreCase(string)) {
return Boolean.FALSE; return Boolean.FALSE;
} }
if (string.equalsIgnoreCase("null")) { if ("null".equalsIgnoreCase(string)) {
return JSONObject.NULL; return JSONObject.NULL;
} }
@@ -2177,7 +2194,8 @@ public class JSONObject {
char initial = string.charAt(0); char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') { if ((initial >= '0' && initial <= '9') || initial == '-') {
try { try {
// if we want full Big Number support this block can be replaced with: // if we want full Big Number support the contents of this
// `try` block can be replaced with:
// return stringToNumber(string); // return stringToNumber(string);
if (isDecimalNotation(string)) { if (isDecimalNotation(string)) {
Double d = Double.valueOf(string); Double d = Double.valueOf(string);
@@ -2414,13 +2432,9 @@ public class JSONObject {
} else if (value instanceof Number) { } else if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString((Number) value); final String numberAsString = numberToString((Number) value);
try { if(NUMBER_PATTERN.matcher(numberAsString).matches()) {
// Use the BigDecimal constructor for its parser to validate the format.
@SuppressWarnings("unused")
BigDecimal testNum = new BigDecimal(numberAsString);
// Close enough to a JSON number that we will use it unquoted
writer.write(numberAsString); writer.write(numberAsString);
} catch (NumberFormatException ex){ } else {
// The Number value is not a valid JSON number. // The Number value is not a valid JSON number.
// Instead we will quote it as a string // Instead we will quote it as a string
quote(numberAsString, writer); quote(numberAsString, writer);
@@ -2482,7 +2496,7 @@ public class JSONObject {
public Writer write(Writer writer, int indentFactor, int indent) public Writer write(Writer writer, int indentFactor, int indent)
throws JSONException { throws JSONException {
try { try {
boolean commanate = false; boolean needsComma = false;
final int length = this.length(); final int length = this.length();
writer.write('{'); writer.write('{');
@@ -2500,15 +2514,15 @@ public class JSONObject {
throw new JSONException("Unable to write JSONObject value for key: " + key, e); throw new JSONException("Unable to write JSONObject value for key: " + key, e);
} }
} else if (length != 0) { } else if (length != 0) {
final int newindent = indent + indentFactor; final int newIndent = indent + indentFactor;
for (final Entry<String,?> entry : this.entrySet()) { for (final Entry<String,?> entry : this.entrySet()) {
if (commanate) { if (needsComma) {
writer.write(','); writer.write(',');
} }
if (indentFactor > 0) { if (indentFactor > 0) {
writer.write('\n'); writer.write('\n');
} }
indent(writer, newindent); indent(writer, newIndent);
final String key = entry.getKey(); final String key = entry.getKey();
writer.write(quote(key)); writer.write(quote(key));
writer.write(':'); writer.write(':');
@@ -2516,11 +2530,11 @@ public class JSONObject {
writer.write(' '); writer.write(' ');
} }
try { try {
writeValue(writer, entry.getValue(), indentFactor, newindent); writeValue(writer, entry.getValue(), indentFactor, newIndent);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("Unable to write JSONObject value for key: " + key, e); throw new JSONException("Unable to write JSONObject value for key: " + key, e);
} }
commanate = true; needsComma = true;
} }
if (indentFactor > 0) { if (indentFactor > 0) {
writer.write('\n'); writer.write('\n');
@@ -2560,4 +2574,37 @@ public class JSONObject {
} }
return results; return results;
} }
/**
* Create a new JSONException in a common format for incorrect conversions.
* @param key name of the key
* @param valueType the type of value being coerced to
* @param cause optional cause of the coercion failure
* @return JSONException that can be thrown.
*/
private static JSONException wrongValueFormatException(
String key,
String valueType,
Throwable cause) {
return new JSONException(
"JSONObject[" + quote(key) + "] is not a " + valueType + "."
, cause);
}
/**
* Create a new JSONException in a common format for incorrect conversions.
* @param key name of the key
* @param valueType the type of value being coerced to
* @param cause optional cause of the coercion failure
* @return JSONException that can be thrown.
*/
private static JSONException wrongValueFormatException(
String key,
String valueType,
Object value,
Throwable cause) {
return new JSONException(
"JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value + ")."
, cause);
}
} }

View File

@@ -186,7 +186,7 @@ public class JSONPointer {
this.refTokens = new ArrayList<String>(refTokens); this.refTokens = new ArrayList<String>(refTokens);
} }
private String unescape(String token) { private static String unescape(String token) {
return token.replace("~1", "/").replace("~0", "~") return token.replace("~1", "/").replace("~0", "~")
.replace("\\\"", "\"") .replace("\\\"", "\"")
.replace("\\\\", "\\"); .replace("\\\\", "\\");
@@ -228,13 +228,13 @@ public class JSONPointer {
* @return the matched object. If no matching item is found a * @return the matched object. If no matching item is found a
* @throws JSONPointerException is thrown if the index is out of bounds * @throws JSONPointerException is thrown if the index is out of bounds
*/ */
private Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
try { try {
int index = Integer.parseInt(indexToken); int index = Integer.parseInt(indexToken);
JSONArray currentArr = (JSONArray) current; JSONArray currentArr = (JSONArray) current;
if (index >= currentArr.length()) { if (index >= currentArr.length()) {
throw new JSONPointerException(format("index %d is out of bounds - the array has %d elements", index, throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
currentArr.length())); Integer.valueOf(currentArr.length())));
} }
try { try {
return currentArr.get(index); return currentArr.get(index);
@@ -267,7 +267,7 @@ public class JSONPointer {
* @param token the JSONPointer segment value to be escaped * @param token the JSONPointer segment value to be escaped
* @return the escaped value for the token * @return the escaped value for the token
*/ */
private String escape(String token) { private static String escape(String token) {
return token.replace("~", "~0") return token.replace("~", "~0")
.replace("/", "~1") .replace("/", "~1")
.replace("\\", "\\\\") .replace("\\", "\\\\")

View File

@@ -1,4 +1,29 @@
package org.json; package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/** /**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code> * The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of * method so that a class can change the behavior of

View File

@@ -448,7 +448,9 @@ public class JSONTokener {
sb.append(c); sb.append(c);
c = this.next(); c = this.next();
} }
this.back(); if (!this.eof) {
this.back();
}
string = sb.toString().trim(); string = sb.toString().trim();
if ("".equals(string)) { if ("".equals(string)) {

View File

@@ -1,7 +1,6 @@
package org.json; package org.json;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@@ -326,31 +325,27 @@ public class JSONWriter {
return "null"; return "null";
} }
if (value instanceof JSONString) { if (value instanceof JSONString) {
Object object; String object;
try { try {
object = ((JSONString) value).toJSONString(); object = ((JSONString) value).toJSONString();
} catch (Exception e) { } catch (Exception e) {
throw new JSONException(e); throw new JSONException(e);
} }
if (object instanceof String) { if (object != null) {
return (String) object; return object;
} }
throw new JSONException("Bad value from toJSONString: " + object); throw new JSONException("Bad value from toJSONString: " + object);
} }
if (value instanceof Number) { if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
final String numberAsString = JSONObject.numberToString((Number) value); final String numberAsString = JSONObject.numberToString((Number) value);
try { if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) {
// Use the BigDecimal constructor for it's parser to validate the format.
@SuppressWarnings("unused")
BigDecimal unused = new BigDecimal(numberAsString);
// Close enough to a JSON number that we will return it unquoted // Close enough to a JSON number that we will return it unquoted
return numberAsString; return numberAsString;
} catch (NumberFormatException ex){
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
return JSONObject.quote(numberAsString);
} }
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
return JSONObject.quote(numberAsString);
} }
if (value instanceof Boolean || value instanceof JSONObject if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) { || value instanceof JSONArray) {

View File

@@ -3,6 +3,8 @@ JSON in Java [package org.json]
[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json) [![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json)
**[Click here if you just want the latest release jar file.](https://repo1.maven.org/maven2/org/json/json/20190722/json-20190722.jar)**
JSON is a light-weight, language independent, data interchange format. JSON is a light-weight, language independent, data interchange format.
See http://www.JSON.org/ See http://www.JSON.org/
@@ -30,7 +32,9 @@ to produce a vector-like object. The object provides methods for manipulating
its contents, and for producing a JSON compliant array serialization. its contents, and for producing a JSON compliant array serialization.
**JSONTokener.java**: The `JSONTokener` breaks a text into a sequence of individual **JSONTokener.java**: The `JSONTokener` breaks a text into a sequence of individual
tokens. It can be constructed from a `String`, `Reader`, or `InputStream`. tokens. It can be constructed from a `String`, `Reader`, or `InputStream`. It also can
parse text from a `String`, `Number`, `Boolean` or `null` like `"hello"`, `42`, `true`,
`null` to produce a simple json object.
**JSONException.java**: The `JSONException` is the standard exception type thrown **JSONException.java**: The `JSONException` is the standard exception type thrown
by this package. by this package.
@@ -86,7 +90,7 @@ https://github.com/stleary/JSON-Java-unit-test
Numeric types in this package comply with Numeric types in this package comply with
[ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) and [ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) and
[RFC 7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159#section-6). [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259#section-6).
This package fully supports `Integer`, `Long`, and `Double` Java types. Partial support This package fully supports `Integer`, `Long`, and `Double` Java types. Partial support
for `BigInteger` and `BigDecimal` values in `JSONObject` and `JSONArray` objects is provided for `BigInteger` and `BigDecimal` values in `JSONObject` and `JSONArray` objects is provided
in the form of `get()`, `opt()`, and `put()` API methods. in the form of `get()`, `opt()`, and `put()` API methods.
@@ -94,17 +98,27 @@ in the form of `get()`, `opt()`, and `put()` API methods.
Although 1.6 compatibility is currently supported, it is not a project goal and may be Although 1.6 compatibility is currently supported, it is not a project goal and may be
removed in some future release. removed in some future release.
In compliance with RFC7159 page 10 section 9, the parser is more lax with what is valid In compliance with RFC8259 page 10 section 9, the parser is more lax with what is valid
JSON than the Generator. For Example, the tab character (U+0009) is allowed when reading JSON than the Generator. For Example, the tab character (U+0009) is allowed when reading
JSON Text strings, but when output by the Generator, tab is properly converted to \t in JSON Text strings, but when output by the Generator, tab is properly converted to \t in
the string. Other instances may occur where reading invalid JSON text does not cause an the string. Other instances may occur where reading invalid JSON text does not cause an
error to be generated. Malformed JSON Texts such as missing end " (quote) on strings or error to be generated. Malformed JSON Texts such as missing end " (quote) on strings or
invalid number formats (1.2e6.3) will cause errors as such documents can not be read invalid number formats (1.2e6.3) will cause errors as such documents can not be read
reliably. reliably.
Some notible exceptions that the JSON Parser in this library accepts are:
* Unquoted keys `{ key: "value" }`
* Unquoted values `{ "key": value }`
* Unescaped literals like "tab" in string values `{ "key": "value with an unescaped tab" }`
* Numbers out of range for `Double` or `Long` are parsed as strings
Release history: Release history:
~~~ ~~~
20200518 Recent commits and snapshot before project structure change
20190722 Recent commits
20180813 POM change to include Automatic-Module-Name (#431) 20180813 POM change to include Automatic-Module-Name (#431)
20180130 Recent commits 20180130 Recent commits
@@ -133,4 +147,4 @@ as of 29 July, 2015.
JSON-java releases can be found by searching the Maven repository for groupId "org.json" JSON-java releases can be found by searching the Maven repository for groupId "org.json"
and artifactId "json". For example: and artifactId "json". For example:
https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.json%22%20AND%20a%3A%22json%22 https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav

181
XML.java
View File

@@ -31,12 +31,13 @@ import java.util.Iterator;
/** /**
* This provides static methods to convert an XML text into a JSONObject, and to * This provides static methods to convert an XML text into a JSONObject, and to
* covert a JSONObject into an XML text. * covert a JSONObject into an XML text.
* *
* @author JSON.org * @author JSON.org
* @version 2016-08-10 * @version 2016-08-10
*/ */
@SuppressWarnings("boxing") @SuppressWarnings("boxing")
public class XML { public class XML {
/** The Character '&amp;'. */ /** The Character '&amp;'. */
public static final Character AMP = '&'; public static final Character AMP = '&';
@@ -63,7 +64,12 @@ public class XML {
/** The Character '/'. */ /** The Character '/'. */
public static final Character SLASH = '/'; public static final Character SLASH = '/';
/**
* Null attribute name
*/
public static final String NULL_ATTR = "xsi:nil";
/** /**
* Creates an iterator for navigating Code Points in a string instead of * Creates an iterator for navigating Code Points in a string instead of
* characters. Once Java7 support is dropped, this can be replaced with * characters. Once Java7 support is dropped, this can be replaced with
@@ -71,7 +77,7 @@ public class XML {
* string.codePoints() * string.codePoints()
* </code> * </code>
* which is available in Java8 and above. * which is available in Java8 and above.
* *
* @see <a href= * @see <a href=
* "http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888</a> * "http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888</a>
*/ */
@@ -106,7 +112,7 @@ public class XML {
/** /**
* Replace special characters with XML escapes: * Replace special characters with XML escapes:
* *
* <pre> * <pre>
* &amp; <small>(ampersand)</small> is replaced by &amp;amp; * &amp; <small>(ampersand)</small> is replaced by &amp;amp;
* &lt; <small>(less than)</small> is replaced by &amp;lt; * &lt; <small>(less than)</small> is replaced by &amp;lt;
@@ -114,7 +120,7 @@ public class XML {
* &quot; <small>(double quote)</small> is replaced by &amp;quot; * &quot; <small>(double quote)</small> is replaced by &amp;quot;
* &apos; <small>(single quote / apostrophe)</small> is replaced by &amp;apos; * &apos; <small>(single quote / apostrophe)</small> is replaced by &amp;apos;
* </pre> * </pre>
* *
* @param string * @param string
* The string to be escaped. * The string to be escaped.
* @return The escaped string. * @return The escaped string.
@@ -150,17 +156,17 @@ public class XML {
} }
return sb.toString(); return sb.toString();
} }
/** /**
* @param cp code point to test * @param cp code point to test
* @return true if the code point is not valid for an XML * @return true if the code point is not valid for an XML
*/ */
private static boolean mustEscape(int cp) { private static boolean mustEscape(int cp) {
/* Valid range from https://www.w3.org/TR/REC-xml/#charsets /* Valid range from https://www.w3.org/TR/REC-xml/#charsets
* *
* #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
* *
* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
*/ */
// isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F) // isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F)
// all ISO control characters are out of range except tabs and new lines // all ISO control characters are out of range except tabs and new lines
@@ -179,7 +185,7 @@ public class XML {
/** /**
* Removes XML escapes from the string. * Removes XML escapes from the string.
* *
* @param string * @param string
* string to remove escapes from * string to remove escapes from
* @return string with converted entities * @return string with converted entities
@@ -211,7 +217,7 @@ public class XML {
/** /**
* Throw an exception if the string contains whitespace. Whitespace is not * Throw an exception if the string contains whitespace. Whitespace is not
* allowed in tagNames and attributes. * allowed in tagNames and attributes.
* *
* @param string * @param string
* A string. * A string.
* @throws JSONException Thrown if the string contains whitespace or is empty. * @throws JSONException Thrown if the string contains whitespace or is empty.
@@ -231,7 +237,7 @@ public class XML {
/** /**
* Scan the content following the named tag, attaching it to the context. * Scan the content following the named tag, attaching it to the context.
* *
* @param x * @param x
* The XMLTokener containing the source string. * The XMLTokener containing the source string.
* @param context * @param context
@@ -241,11 +247,11 @@ public class XML {
* @return true if the close tag is processed. * @return true if the close tag is processed.
* @throws JSONException * @throws JSONException
*/ */
private static boolean parse(XMLTokener x, JSONObject context, String name, boolean keepStrings) private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config)
throws JSONException { throws JSONException {
char c; char c;
int i; int i;
JSONObject jsonobject = null; JSONObject jsonObject = null;
String string; String string;
String tagName; String tagName;
Object token; Object token;
@@ -278,7 +284,7 @@ public class XML {
if (x.next() == '[') { if (x.next() == '[') {
string = x.nextCDATA(); string = x.nextCDATA();
if (string.length() > 0) { if (string.length() > 0) {
context.accumulate("content", string); context.accumulate(config.cDataTagName, string);
} }
return false; return false;
} }
@@ -326,7 +332,8 @@ public class XML {
} else { } else {
tagName = (String) token; tagName = (String) token;
token = null; token = null;
jsonobject = new JSONObject(); jsonObject = new JSONObject();
boolean nilAttributeFound = false;
for (;;) { for (;;) {
if (token == null) { if (token == null) {
token = x.nextToken(); token = x.nextToken();
@@ -340,11 +347,20 @@ public class XML {
if (!(token instanceof String)) { if (!(token instanceof String)) {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
jsonobject.accumulate(string,
keepStrings ? ((String)token) : stringToValue((String) token)); if (config.convertNilAttributeToNull
&& NULL_ATTR.equals(string)
&& Boolean.parseBoolean((String) token)) {
nilAttributeFound = true;
} else if (!nilAttributeFound) {
jsonObject.accumulate(string,
config.keepStrings
? ((String) token)
: stringToValue((String) token));
}
token = null; token = null;
} else { } else {
jsonobject.accumulate(string, ""); jsonObject.accumulate(string, "");
} }
@@ -353,8 +369,10 @@ public class XML {
if (x.nextToken() != GT) { if (x.nextToken() != GT) {
throw x.syntaxError("Misshaped tag"); throw x.syntaxError("Misshaped tag");
} }
if (jsonobject.length() > 0) { if (nilAttributeFound) {
context.accumulate(tagName, jsonobject); context.accumulate(tagName, JSONObject.NULL);
} else if (jsonObject.length() > 0) {
context.accumulate(tagName, jsonObject);
} else { } else {
context.accumulate(tagName, ""); context.accumulate(tagName, "");
} }
@@ -372,21 +390,20 @@ public class XML {
} else if (token instanceof String) { } else if (token instanceof String) {
string = (String) token; string = (String) token;
if (string.length() > 0) { if (string.length() > 0) {
jsonobject.accumulate("content", jsonObject.accumulate(config.cDataTagName,
keepStrings ? string : stringToValue(string)); config.keepStrings ? string : stringToValue(string));
} }
} else if (token == LT) { } else if (token == LT) {
// Nested element // Nested element
if (parse(x, jsonobject, tagName,keepStrings)) { if (parse(x, jsonObject, tagName, config)) {
if (jsonobject.length() == 0) { if (jsonObject.length() == 0) {
context.accumulate(tagName, ""); context.accumulate(tagName, "");
} else if (jsonobject.length() == 1 } else if (jsonObject.length() == 1
&& jsonobject.opt("content") != null) { && jsonObject.opt(config.cDataTagName) != null) {
context.accumulate(tagName, context.accumulate(tagName, jsonObject.opt(config.cDataTagName));
jsonobject.opt("content"));
} else { } else {
context.accumulate(tagName, jsonobject); context.accumulate(tagName, jsonObject);
} }
return false; return false;
} }
@@ -398,10 +415,10 @@ public class XML {
} }
} }
} }
/** /**
* This method is the same as {@link JSONObject#stringToValue(String)}. * This method is the same as {@link JSONObject#stringToValue(String)}.
* *
* @param string String to convert * @param string String to convert
* @return JSON value of this string or the string * @return JSON value of this string or the string
*/ */
@@ -462,14 +479,14 @@ public class XML {
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> * "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored. * are ignored.
* *
* @param string * @param string
* The source string. * The source string.
* @return A JSONObject containing the structured data from the XML string. * @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string * @throws JSONException Thrown if there is an errors while parsing the string
*/ */
public static JSONObject toJSONObject(String string) throws JSONException { public static JSONObject toJSONObject(String string) throws JSONException {
return toJSONObject(string, false); return toJSONObject(string, XMLParserConfiguration.ORIGINAL);
} }
/** /**
@@ -488,7 +505,7 @@ public class XML {
* @throws JSONException Thrown if there is an errors while parsing the string * @throws JSONException Thrown if there is an errors while parsing the string
*/ */
public static JSONObject toJSONObject(Reader reader) throws JSONException { public static JSONObject toJSONObject(Reader reader) throws JSONException {
return toJSONObject(reader, false); return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
} }
/** /**
@@ -512,12 +529,38 @@ public class XML {
* @throws JSONException Thrown if there is an errors while parsing the string * @throws JSONException Thrown if there is an errors while parsing the string
*/ */
public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException { public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException {
if(keepStrings) {
return toJSONObject(reader, XMLParserConfiguration.KEEP_STRINGS);
}
return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
}
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored.
*
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document.
*
* @param reader The XML source reader.
* @param config Configuration options for the parser
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration config) throws JSONException {
JSONObject jo = new JSONObject(); JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(reader); XMLTokener x = new XMLTokener(reader);
while (x.more()) { while (x.more()) {
x.skipPast("<"); x.skipPast("<");
if(x.more()) { if(x.more()) {
parse(x, jo, null, keepStrings); parse(x, jo, null, config);
} }
} }
return jo; return jo;
@@ -533,10 +576,10 @@ public class XML {
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> * "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored. * are ignored.
* *
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to * All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document. * numbers but will instead be the exact value as seen in the XML document.
* *
* @param string * @param string
* The source string. * The source string.
* @param keepStrings If true, then values will not be coerced into boolean * @param keepStrings If true, then values will not be coerced into boolean
@@ -548,21 +591,45 @@ public class XML {
return toJSONObject(new StringReader(string), keepStrings); return toJSONObject(new StringReader(string), keepStrings);
} }
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored.
*
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document.
*
* @param string
* The source string.
* @param config Configuration options for the parser.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string, XMLParserConfiguration config) throws JSONException {
return toJSONObject(new StringReader(string), config);
}
/** /**
* Convert a JSONObject into a well-formed, element-normal XML string. * Convert a JSONObject into a well-formed, element-normal XML string.
* *
* @param object * @param object
* A JSONObject. * A JSONObject.
* @return A string. * @return A string.
* @throws JSONException Thrown if there is an error parsing the string * @throws JSONException Thrown if there is an error parsing the string
*/ */
public static String toString(Object object) throws JSONException { public static String toString(Object object) throws JSONException {
return toString(object, null); return toString(object, null, XMLParserConfiguration.ORIGINAL);
} }
/** /**
* Convert a JSONObject into a well-formed, element-normal XML string. * Convert a JSONObject into a well-formed, element-normal XML string.
* *
* @param object * @param object
* A JSONObject. * A JSONObject.
* @param tagName * @param tagName
@@ -570,7 +637,23 @@ public class XML {
* @return A string. * @return A string.
* @throws JSONException Thrown if there is an error parsing the string * @throws JSONException Thrown if there is an error parsing the string
*/ */
public static String toString(final Object object, final String tagName) public static String toString(final Object object, final String tagName) {
return toString(object, tagName, XMLParserConfiguration.ORIGINAL);
}
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
*
* @param object
* A JSONObject.
* @param tagName
* The optional name of the enclosing tag.
* @param config
* Configuration that can control output to XML.
* @return A string.
* @throws JSONException Thrown if there is an error parsing the string
*/
public static String toString(final Object object, final String tagName, final XMLParserConfiguration config)
throws JSONException { throws JSONException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
JSONArray ja; JSONArray ja;
@@ -598,7 +681,7 @@ public class XML {
} }
// Emit content in body // Emit content in body
if ("content".equals(key)) { if (key.equals(config.cDataTagName)) {
if (value instanceof JSONArray) { if (value instanceof JSONArray) {
ja = (JSONArray) value; ja = (JSONArray) value;
int jaLength = ja.length(); int jaLength = ja.length();
@@ -626,12 +709,12 @@ public class XML {
sb.append('<'); sb.append('<');
sb.append(key); sb.append(key);
sb.append('>'); sb.append('>');
sb.append(toString(val)); sb.append(toString(val, null, config));
sb.append("</"); sb.append("</");
sb.append(key); sb.append(key);
sb.append('>'); sb.append('>');
} else { } else {
sb.append(toString(val, key)); sb.append(toString(val, key, config));
} }
} }
} else if ("".equals(value)) { } else if ("".equals(value)) {
@@ -642,12 +725,12 @@ public class XML {
// Emit a new tag <k> // Emit a new tag <k>
} else { } else {
sb.append(toString(value, key)); sb.append(toString(value, key, config));
} }
} }
if (tagName != null) { if (tagName != null) {
// Emit the </tagname> close tag // Emit the </tagName> close tag
sb.append("</"); sb.append("</");
sb.append(tagName); sb.append(tagName);
sb.append('>'); sb.append('>');
@@ -669,7 +752,7 @@ public class XML {
// XML does not have good support for arrays. If an array // XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an // appears in a place where XML is lacking, synthesize an
// <array> element. // <array> element.
sb.append(toString(val, tagName == null ? "array" : tagName)); sb.append(toString(val, tagName == null ? "array" : tagName, config));
} }
return sb.toString(); return sb.toString();
} }

107
XMLParserConfiguration.java Normal file
View File

@@ -0,0 +1,107 @@
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* Configuration object for the XML parser.
* @author AylwardJ
*
*/
public class XMLParserConfiguration {
/** Original Configuration of the XML Parser. */
public static final XMLParserConfiguration ORIGINAL = new XMLParserConfiguration();
/** Original configuration of the XML Parser except that values are kept as strings. */
public static final XMLParserConfiguration KEEP_STRINGS = new XMLParserConfiguration(true);
/**
* When parsing the XML into JSON, specifies if values should be kept as strings (true), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*/
public final boolean keepStrings;
/**
* The name of the key in a JSON Object that indicates a CDATA section. Historically this has
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA
* processing.
*/
public final String cDataTagName;
/**
* When parsing the XML into JSON, specifies if values with attribute xsi:nil="true"
* should be kept as attribute(false), or they should be converted to null(true)
*/
public final boolean convertNilAttributeToNull;
/**
* Default parser configuration. Does not keep strings, and the CDATA Tag Name is "content".
*/
public XMLParserConfiguration () {
this(false, "content", false);
}
/**
* Configure the parser string processing and use the default CDATA Tag Name as "content".
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
*/
public XMLParserConfiguration (final boolean keepStrings) {
this(keepStrings, "content", false);
}
/**
* Configure the parser string processing to try and convert XML values to JSON values and
* use the passed CDATA Tag Name the processing value. Pass <code>null</code> to
* disable CDATA processing
* @param cDataTagName<code>null</code> to disable CDATA processing. Any other value
* to use that value as the JSONObject key name to process as CDATA.
*/
public XMLParserConfiguration (final String cDataTagName) {
this(false, cDataTagName, false);
}
/**
* Configure the parser to use custom settings.
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
* @param cDataTagName<code>null</code> to disable CDATA processing. Any other value
* to use that value as the JSONObject key name to process as CDATA.
*/
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
this.keepStrings = keepStrings;
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = false;
}
/**
* Configure the parser to use custom settings.
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
* @param cDataTagName <code>null</code> to disable CDATA processing. Any other value
* to use that value as the JSONObject key name to process as CDATA.
* @param convertNilAttributeToNull <code>true</code> to parse values with attribute xsi:nil="true" as null.
* <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}.
*/
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
this.keepStrings = keepStrings;
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
}
}

View File

@@ -135,7 +135,7 @@ public class XMLTokener extends JSONTokener {
* @return A Character or an entity String if the entity is not recognized. * @return A Character or an entity String if the entity is not recognized.
* @throws JSONException If missing ';' in XML entity. * @throws JSONException If missing ';' in XML entity.
*/ */
public Object nextEntity(char ampersand) throws JSONException { public Object nextEntity(@SuppressWarnings("unused") char ampersand) throws JSONException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (;;) { for (;;) {
char c = next(); char c = next();
@@ -152,7 +152,7 @@ public class XMLTokener extends JSONTokener {
} }
/** /**
* Unescapes an XML entity encoding; * Unescape an XML entity encoding;
* @param e entity (only the actual entity value, not the preceding & or ending ; * @param e entity (only the actual entity value, not the preceding & or ending ;
* @return * @return
*/ */
@@ -232,6 +232,7 @@ public class XMLTokener extends JSONTokener {
} }
switch (c) { switch (c) {
case 0: case 0:
throw syntaxError("Unterminated string");
case '<': case '<':
case '>': case '>':
case '/': case '/':