36 Commits

Author SHA1 Message Date
Sean Leary
cbd8b18c4a Update README 2017-05-16 00:29:00 -05:00
Sean Leary
f12fa9ba5f Update LICENSE 2017-04-18 08:32:10 -05:00
Sean Leary
80e2ea2a80 Merge pull request #324 from dtalex/JSONPointerOnBeans
Allow user to invoke query and optQuery ,with a JSONPointer
2017-03-27 20:04:18 -05:00
alessandro rao
2917104b53 Allow user to invoke query and optQuery ,with a JSONPointer,directly
from JSONArray or JSONObject fix JSONArray
2017-02-25 14:35:02 +01:00
alessandro rao
9e0fc5e680 Allow user to invoke query and optQuery ,with a JSONPointer,directly
from JSONArray or JSONObject
2017-02-25 13:27:50 +01:00
Sean Leary
724fb888f7 Merge pull request #317 from johnjaylward/fixLocale
make sure locale independent data is not upper/lowercased incorrectly…
2017-02-19 21:34:17 -06:00
John J. Aylward
eb806f4c14 make sure locale independent data is not upper/lowercased incorrectly. See #315 2017-02-10 10:07:28 -05:00
Sean Leary
5ff8b4cb08 Merge pull request #304 from omarzina/master
[FIX] Update README
2016-12-05 08:58:35 -06:00
joumar
5ef4f58ef1 [FIX] Update README
Fixed C&P typo
2016-12-05 11:55:24 -03:00
Sean Leary
413bb53b48 Merge pull request #288 from johnjaylward/XmlEscape
Bug fixes for XML Encoding and Decoding
2016-11-24 10:01:14 -06:00
Sean Leary
237376eca6 Merge pull request #292 from erosb/master
Provides "#" string evaluation support for JSON Pointer
2016-10-08 10:07:31 -05:00
Bence Erős
e0616a129e fixing #291 2016-10-05 14:57:42 +02:00
John J. Aylward
93ffca36c3 fixes spacing 2016-09-28 20:23:30 -04:00
John J. Aylward
e477d7002b fixes object comparison 2016-09-28 20:22:12 -04:00
John J. Aylward
fb1db9341e Changes encoding to better match the XML spec section 2.2 2016-09-28 20:15:58 -04:00
John J. Aylward
adb0478f66 properly unescape tokens in JSONML for reversability. 2016-09-22 16:23:09 -04:00
John J. Aylward
f58a0f4684 fixes code point appends to string builder 2016-09-22 16:10:49 -04:00
John J. Aylward
c11e09959c Fixes code point output when unescaping code points. XML escapes are an entire code point, not surrogate pairs like in JSON. 2016-09-22 15:40:26 -04:00
John J. Aylward
68f92eb395 Adds more javadoc. 2016-09-22 14:40:39 -04:00
John J. Aylward
34652a8706 Updates to iterate on code points instead of characters and changes the encoding to only encode control characters as defined by ISO standard. 2016-09-22 14:13:14 -04:00
John J. Aylward
a2d3b59394 Implements unicode escaping similar to JSONObject.
* Removes deprecation on XML.stringToValue(). It now provides unescaping for strings to convert XML entities back into values.
* New unescape function to handle XML entities -> value conversion.
2016-09-22 12:38:06 -04:00
Sean Leary
c24be0e4ea Merge pull request #274 from johnjaylward/NumberOutputFix
Fix for number output bug.
2016-09-09 11:42:35 -05:00
John J. Aylward
88f65c5bea Merge branch 'master' of github.com:stleary/JSON-java into NumberOutputFix
# Conflicts:
#	JSONObject.java
2016-08-26 11:32:35 -04:00
Sean Leary
ebe69df8e4 Merge pull request #271 from johnjaylward/EnumCleanup
Update enum support to be more fully featured.
2016-08-19 10:28:04 -05:00
John J. Aylward
2f2cd4dfc5 Fix for number output bug.
java.lang.Number is currently output without any validation. For all java.* Numbers, this is fine, but for custom Number implementations like Complex or Fraction, the resulting JSON output may be invalid.

For example: If a Fraction implementation defines its' toString method as `return numerator + "/" + denominator`, then the resulting JSON output would be something like this:

```json
{ "fraction" : 1/2 }
```

This is not valid JSON.

This commit verifies that the string representation of the number is close to a JSON formatted number by use of the BigDecimal constructor. If the constructor throws a NumberFormatException, then the string value is instead quoted as a string. The example above would instead output like the following:

```json
{ "fraction" : "1/2" }
```
2016-08-17 12:54:30 -04:00
John J. Aylward
349a209df3 Merge remote-tracking branch 'upstream/master' into EnumCleanup 2016-08-15 10:25:27 -04:00
John J. Aylward
7851e9b2e8 revert back changes to Number support 2016-08-15 10:24:38 -04:00
Sean Leary
7232a95c0b Update JSONObject.java
Fixed some typos committed in #249, since reverted, tracking issue is #263
2016-08-15 01:58:54 -05:00
Sean Leary
f96f505188 Update JSONArray.java
Fixed a Javadoc typo, originally fixed in #249, since reverted. This is to address issue #263
2016-08-15 01:53:08 -05:00
John J. Aylward
91107e3e82 Adds support to JSONObject wrap and write methods to explicitly handle Enums.
The new way enums are handled is to always place the actual enum in the
JSONObject/JSONArray. When writing, we always write the actual "name"
of the enum, so even with a toString override on the enum class, the
value remains consistant and compatible with the optEnum/getEnum methods.

The constructor JSONObject(Object) functions the same way as before when
passing an enum and is consistent with other "value" types. For example,
when creating a JSONObject with Long, Boolean, BigDecimal as the constructor
parameter, the value will be treated as a "bean".
2016-08-11 12:22:31 -04:00
Sean Leary
4e8e24d49d Merge pull request #259 from run2000/appendable_v2
JSONWriter uses Appendable (v2)
2016-08-10 21:55:10 -05:00
Sean Leary
f881b61c81 Update XML.java
Removed a problematic JavaDoc in the header comment to a deprecated method
2016-08-10 15:35:26 -05:00
Sean Leary
37582a44ad Update README 2016-08-10 09:13:22 -05:00
Sean Leary
154cfda9aa Merge pull request #261 from stleary/revert-249-master
Revert "reduces the use of unnecessary exceptions"
2016-08-10 09:03:57 -05:00
Sean Leary
45a7decba4 Revert "reduces the use of unnecessary exceptions" 2016-08-09 14:22:06 -05:00
Nicholas Cull
0c157cae75 JSONWriter uses Appendable. 2016-08-08 19:40:10 +10:00
9 changed files with 338 additions and 219 deletions

View File

@@ -25,6 +25,7 @@ SOFTWARE.
*/
import java.util.Iterator;
import java.util.Locale;
/**
* Convert an HTTP header to a JSONObject and back.
@@ -74,7 +75,7 @@ public class HTTP {
String token;
token = x.nextToken();
if (token.toUpperCase().startsWith("HTTP")) {
if (token.toUpperCase(Locale.ROOT).startsWith("HTTP")) {
// Response

View File

@@ -78,7 +78,7 @@ import java.util.Map;
* </ul>
*
* @author JSON.org
* @version 2016-07-19
* @version 2016-08/15
*/
public class JSONArray implements Iterable<Object> {
@@ -241,16 +241,12 @@ public class JSONArray implements Iterable<Object> {
public double getDouble(int index) throws JSONException {
Object object = this.get(index);
try {
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else if (object instanceof String) {
return Double.parseDouble((String) object);
}
return object instanceof Number ? ((Number) object).doubleValue()
: Double.parseDouble((String) object);
} catch (Exception e) {
}
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the enum value associated with an index.
@@ -329,16 +325,12 @@ public class JSONArray implements Iterable<Object> {
public int getInt(int index) throws JSONException {
Object object = this.get(index);
try {
if (object instanceof Number) {
return ((Number) object).intValue();
} else if (object instanceof String) {
return Integer.parseInt((String) object);
}
return object instanceof Number ? ((Number) object).intValue()
: Integer.parseInt((String) object);
} catch (Exception e) {
}
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the JSONArray associated with an index.
@@ -389,16 +381,12 @@ public class JSONArray implements Iterable<Object> {
public long getLong(int index) throws JSONException {
Object object = this.get(index);
try {
if (object instanceof Number) {
return ((Number) object).longValue();
} else if (object instanceof String) {
return Long.parseLong((String) object);
}
return object instanceof Number ? ((Number) object).longValue()
: Long.parseLong((String) object);
} catch (Exception e) {
}
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the string associated with an index.
@@ -498,20 +486,11 @@ public class JSONArray implements Iterable<Object> {
* @return The truth.
*/
public boolean optBoolean(int index, boolean defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
try {
return this.getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
if (object.equals(Boolean.FALSE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("false"))) {
return false;
} else if (object.equals(Boolean.TRUE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("true"))) {
return true;
}
return defaultValue;
}
/**
@@ -539,21 +518,12 @@ public class JSONArray implements Iterable<Object> {
* @return The value.
*/
public double optDouble(int index, double defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else if (object instanceof String) {
return Double.parseDouble((String) object);
}
return this.getDouble(index);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get the optional int value associated with an index. Zero is returned if
@@ -580,21 +550,12 @@ public class JSONArray implements Iterable<Object> {
* @return The value.
*/
public int optInt(int index, int defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).intValue();
} else if (object instanceof String) {
return Integer.parseInt((String) object);
}
return this.getInt(index);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get the enum value associated with a key.
@@ -654,12 +615,8 @@ public class JSONArray implements Iterable<Object> {
* @return The value.
*/
public BigInteger optBigInteger(int index, BigInteger defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
return defaultValue;
}
try {
return new BigInteger(object.toString());
return this.getBigInteger(index);
} catch (Exception e) {
return defaultValue;
}
@@ -677,12 +634,8 @@ public class JSONArray implements Iterable<Object> {
* @return The value.
*/
public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
return defaultValue;
}
try {
return new BigDecimal(object.toString());
return this.getBigDecimal(index);
} catch (Exception e) {
return defaultValue;
}
@@ -740,21 +693,12 @@ public class JSONArray implements Iterable<Object> {
* @return The value.
*/
public long optLong(int index, long defaultValue) {
Object object = this.opt(index);
if (JSONObject.NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).longValue();
} else if (object instanceof String) {
return Long.parseLong((String) object);
}
return this.getLong(index);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an
@@ -1036,7 +980,30 @@ public class JSONArray implements Iterable<Object> {
* @return the item matched by the JSONPointer, otherwise null
*/
public Object query(String jsonPointer) {
return new JSONPointer(jsonPointer).queryFrom(this);
return query(new JSONPointer(jsonPointer));
}
/**
* Uses a uaer initialized JSONPointer and tries to
* match it to an item whithin this JSONArray. For example, given a
* JSONArray initialized with this document:
* <pre>
* [
* {"b":"c"}
* ]
* </pre>
* and this JSONPointer:
* <pre>
* "/0/b"
* </pre>
* Then this method will return the String "c"
* A JSONPointerException may be thrown from code called by this method.
*
* @param jsonPointer string that can be used to create a JSONPointer
* @return the item matched by the JSONPointer, otherwise null
*/
public Object query(JSONPointer jsonPointer) {
return jsonPointer.queryFrom(this);
}
/**
@@ -1048,9 +1015,20 @@ public class JSONArray implements Iterable<Object> {
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/
public Object optQuery(String jsonPointer) {
JSONPointer pointer = new JSONPointer(jsonPointer);
return optQuery(new JSONPointer(jsonPointer));
}
/**
* Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key.
*
* @param The JSON pointer
* @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/
public Object optQuery(JSONPointer jsonPointer) {
try {
return pointer.queryFrom(this);
return jsonPointer.queryFrom(this);
} catch (JSONPointerException e) {
return null;
}
@@ -1137,7 +1115,6 @@ public class JSONArray implements Iterable<Object> {
* @return a printable, displayable, transmittable representation of the
* array.
*/
@Override
public String toString() {
try {
return this.toString(0);

View File

@@ -175,7 +175,7 @@ public class JSONML {
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
newjo.accumulate(attribute, keepStrings ? token :JSONObject.stringToValue((String)token));
newjo.accumulate(attribute, keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token));
token = null;
} else {
newjo.accumulate(attribute, "");
@@ -226,7 +226,7 @@ public class JSONML {
} else {
if (ja != null) {
ja.put(token instanceof String
? keepStrings ? token :JSONObject.stringToValue((String)token)
? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)
: token);
}
}

View File

@@ -93,7 +93,7 @@ import java.util.Set;
* </ul>
*
* @author JSON.org
* @version 2016-07-19
* @version 2016-08-15
*/
public class JSONObject {
/**
@@ -577,16 +577,13 @@ public class JSONObject {
public double getDouble(String key) throws JSONException {
Object object = this.get(key);
try {
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else if (object instanceof String) {
return Double.parseDouble((String) object);
}
return object instanceof Number ? ((Number) object).doubleValue()
: Double.parseDouble((String) object);
} catch (Exception e) {
}
throw new JSONException("JSONObject[" + quote(key)
+ "] is not a number.");
}
}
/**
* Get the int value associated with a key.
@@ -601,15 +598,12 @@ public class JSONObject {
public int getInt(String key) throws JSONException {
Object object = this.get(key);
try {
if (object instanceof Number) {
return ((Number) object).intValue();
} else if (object instanceof String) {
return Integer.parseInt((String) object);
}
return object instanceof Number ? ((Number) object).intValue()
: Integer.parseInt((String) object);
} catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key)
+ "] is not an int.");
}
throw new JSONException("JSONObject[" + quote(key) + "] is not an int.");
}
/**
@@ -661,15 +655,12 @@ public class JSONObject {
public long getLong(String key) throws JSONException {
Object object = this.get(key);
try {
if (object instanceof Number) {
return ((Number) object).longValue();
} else if (object instanceof String) {
return Long.parseLong((String) object);
}
return object instanceof Number ? ((Number) object).longValue()
: Long.parseLong((String) object);
} catch (Exception e) {
throw new JSONException("JSONObject[" + quote(key)
+ "] is not a long.");
}
throw new JSONException("JSONObject[" + quote(key) + "] is not a long.");
}
/**
@@ -942,20 +933,11 @@ public class JSONObject {
* @return The truth.
*/
public boolean optBoolean(String key, boolean defaultValue) {
Object object = this.get(key);
if (NULL.equals(object)) {
try {
return this.getBoolean(key);
} catch (Exception e) {
return defaultValue;
}
if (object.equals(Boolean.FALSE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("false"))) {
return false;
} else if (object.equals(Boolean.TRUE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("true"))) {
return true;
}
return defaultValue;
}
/**
@@ -983,12 +965,8 @@ public class JSONObject {
* @return An object which is the value.
*/
public BigInteger optBigInteger(String key, BigInteger defaultValue) {
Object object = this.get(key);
if (NULL.equals(object)) {
return defaultValue;
}
try {
return new BigInteger(object.toString());
return this.getBigInteger(key);
} catch (Exception e) {
return defaultValue;
}
@@ -1006,12 +984,8 @@ public class JSONObject {
* @return An object which is the value.
*/
public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
Object object = this.opt(key);
if (NULL.equals(object)) {
return defaultValue;
}
try {
return new BigDecimal(object.toString());
return this.getBigDecimal(key);
} catch (Exception e) {
return defaultValue;
}
@@ -1029,21 +1003,12 @@ public class JSONObject {
* @return An object which is the value.
*/
public double optDouble(String key, double defaultValue) {
Object object = this.get(key);
if (NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else if (object instanceof String) {
return Double.parseDouble((String) object);
}
return this.getDouble(key);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get an optional int value associated with a key, or zero if there is no
@@ -1070,21 +1035,12 @@ public class JSONObject {
* @return An object which is the value.
*/
public int optInt(String key, int defaultValue) {
Object object = this.get(key);
if (NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).intValue();
} else if (object instanceof String) {
return Integer.parseInt((String) object);
}
return this.getInt(key);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get an optional JSONArray associated with a key. It returns null if there
@@ -1137,21 +1093,12 @@ public class JSONObject {
* @return An object which is the value.
*/
public long optLong(String key, long defaultValue) {
Object object = this.get(key);
if (NULL.equals(object)) {
return defaultValue;
}
try {
if (object instanceof Number) {
return ((Number) object).longValue();
} else if (object instanceof String) {
return Long.parseLong((String) object);
}
return this.getLong(key);
} catch (Exception e) {
}
return defaultValue;
}
}
/**
* Get an optional string associated with a key. It returns an empty string
@@ -1210,9 +1157,9 @@ public class JSONObject {
&& Character.isUpperCase(key.charAt(0))
&& method.getParameterTypes().length == 0) {
if (key.length() == 1) {
key = key.toLowerCase();
key = key.toLowerCase(Locale.ROOT);
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase()
key = key.substring(0, 1).toLowerCase(Locale.ROOT)
+ key.substring(1);
}
@@ -1412,7 +1359,29 @@ public class JSONObject {
* @return the item matched by the JSONPointer, otherwise null
*/
public Object query(String jsonPointer) {
return new JSONPointer(jsonPointer).queryFrom(this);
return query(new JSONPointer(jsonPointer));
}
/**
* Uses a uaer initialized JSONPointer and tries to
* match it to an item within this JSONObject. For example, given a
* JSONObject initialized with this document:
* <pre>
* {
* "a":{"b":"c"}
* }
* </pre>
* and this JSONPointer:
* <pre>
* "/a/b"
* </pre>
* Then this method will return the String "c".
* A JSONPointerException may be thrown from code called by this method.
*
* @param jsonPointer string that can be used to create a JSONPointer
* @return the item matched by the JSONPointer, otherwise null
*/
public Object query(JSONPointer jsonPointer) {
return jsonPointer.queryFrom(this);
}
/**
@@ -1424,9 +1393,20 @@ public class JSONObject {
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/
public Object optQuery(String jsonPointer) {
JSONPointer pointer = new JSONPointer(jsonPointer);
return optQuery(new JSONPointer(jsonPointer));
}
/**
* Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key.
*
* @param The JSON pointer
* @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/
public Object optQuery(JSONPointer jsonPointer) {
try {
return pointer.queryFrom(this);
return jsonPointer.queryFrom(this);
} catch (JSONPointerException e) {
return null;
}
@@ -1746,7 +1726,18 @@ public class JSONObject {
throw new JSONException("Bad value from toJSONString: " + object);
}
if (value instanceof Number) {
return numberToString((Number) value);
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
final String numberAsString = numberToString((Number) value);
try {
// Use the BigDecimal constructor for it's parser to validate the format.
new BigDecimal(numberAsString);
// Close enough to a JSON number that we will return it unquoted
return numberAsString;
} catch (NumberFormatException ex){
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
return quote(numberAsString);
}
}
if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) {
@@ -1763,6 +1754,9 @@ public class JSONObject {
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
}
if(value instanceof Enum<?>){
return quote(((Enum<?>)value).name());
}
return quote(value.toString());
}
@@ -1790,7 +1784,7 @@ public class JSONObject {
|| object instanceof Long || object instanceof Boolean
|| object instanceof Float || object instanceof Double
|| object instanceof String || object instanceof BigInteger
|| object instanceof BigDecimal) {
|| object instanceof BigDecimal || object instanceof Enum) {
return object;
}
@@ -1836,6 +1830,32 @@ public class JSONObject {
int indentFactor, int indent) throws JSONException, IOException {
if (value == null || value.equals(null)) {
writer.write("null");
} else if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
} else if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString((Number) value);
try {
// Use the BigDecimal constructor for it's 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);
} catch (NumberFormatException ex){
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
quote(numberAsString, writer);
}
} else if (value instanceof Boolean) {
writer.write(value.toString());
} else if (value instanceof Enum<?>) {
writer.write(quote(((Enum<?>)value).name()));
} else if (value instanceof JSONObject) {
((JSONObject) value).write(writer, indentFactor, indent);
} else if (value instanceof JSONArray) {
@@ -1848,18 +1868,6 @@ public class JSONObject {
new JSONArray(coll).write(writer, indentFactor, indent);
} else if (value.getClass().isArray()) {
new JSONArray(value).write(writer, indentFactor, indent);
} else if (value instanceof Number) {
writer.write(numberToString((Number) value));
} else if (value instanceof Boolean) {
writer.write(value.toString());
} else if (value instanceof JSONString) {
Object o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
writer.write(o != null ? o.toString() : quote(value.toString()));
} else {
quote(value.toString(), writer);
}

View File

@@ -138,7 +138,7 @@ public class JSONPointer {
if (pointer == null) {
throw new NullPointerException("pointer cannot be null");
}
if (pointer.isEmpty()) {
if (pointer.isEmpty() || pointer.equals("#")) {
refTokens = Collections.emptyList();
return;
}

View File

@@ -1,7 +1,6 @@
package org.json;
import java.io.IOException;
import java.io.Writer;
/*
Copyright (c) 2006 JSON.org
@@ -50,11 +49,11 @@ SOFTWARE.
* <p>
* The first method called must be <code>array</code> or <code>object</code>.
* There are no methods for adding commas or colons. JSONWriter adds them for
* you. Objects and arrays can be nested up to 20 levels deep.
* you. Objects and arrays can be nested up to 200 levels deep.
* <p>
* This can sometimes be easier than using a JSONObject to build a string.
* @author JSON.org
* @version 2015-12-09
* @version 2016-08-08
*/
public class JSONWriter {
private static final int maxdepth = 200;
@@ -88,12 +87,12 @@ public class JSONWriter {
/**
* The writer that will receive the output.
*/
protected Writer writer;
protected Appendable writer;
/**
* Make a fresh JSONWriter. It can be used to build one JSON text.
*/
public JSONWriter(Writer w) {
public JSONWriter(Appendable w) {
this.comma = false;
this.mode = 'i';
this.stack = new JSONObject[maxdepth];
@@ -114,9 +113,9 @@ public class JSONWriter {
if (this.mode == 'o' || this.mode == 'a') {
try {
if (this.comma && this.mode == 'a') {
this.writer.write(',');
this.writer.append(',');
}
this.writer.write(string);
this.writer.append(string);
} catch (IOException e) {
throw new JSONException(e);
}
@@ -163,7 +162,7 @@ public class JSONWriter {
}
this.pop(mode);
try {
this.writer.write(c);
this.writer.append(c);
} catch (IOException e) {
throw new JSONException(e);
}
@@ -207,10 +206,10 @@ public class JSONWriter {
try {
this.stack[this.top - 1].putOnce(string, Boolean.TRUE);
if (this.comma) {
this.writer.write(',');
this.writer.append(',');
}
this.writer.write(JSONObject.quote(string));
this.writer.write(':');
this.writer.append(JSONObject.quote(string));
this.writer.append(':');
this.comma = false;
this.mode = 'o';
return this;

View File

@@ -1,3 +1,5 @@
============================================================================
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy

9
README
View File

@@ -22,7 +22,7 @@ JSONObject.java: The JSONObject can parse text from a String or a JSONTokener
to produce a map-like object. The object provides methods for manipulating its
contents, and for producing a JSON compliant object serialization.
JSONArray.java: The JSONObject can parse text from a String or a JSONTokener
JSONArray.java: The JSONArray can parse text from a String or a JSONTokener
to produce a vector-like object. The object provides methods for manipulating
its contents, and for producing a JSON compliant array serialization.
@@ -89,8 +89,13 @@ invalid number formats (1.2e6.3) will cause errors as such documents can not be
reliably.
Release history:
20170516 Roll up recent commits.
20160807 Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(),
20160810 Revert code that was breaking opt*() methods.
20160807 This release contains a bug in the JSONObject.opt*() and JSONArray.opt*() methods,
it is not recommended for use.
Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(),
RFC4180 compatibility, JSONPointer, some exception fixes, optional XML type conversion.
Contains the latest code as of 7 Aug, 2016

153
XML.java
View File

@@ -31,11 +31,10 @@ import java.util.Iterator;
* covert a JSONObject into an XML text.
*
* @author JSON.org
* @version 2016-01-30
* @version 2016-08-10
*/
@SuppressWarnings("boxing")
public class XML {
/** The Character '&amp;'. */
public static final Character AMP = '&';
@@ -63,6 +62,46 @@ public class XML {
/** The Character '/'. */
public static final Character SLASH = '/';
/**
* Creates an iterator for navigating Code Points in a string instead of
* characters. Once Java7 support is dropped, this can be replaced with
* <code>
* string.codePoints()
* </code>
* which is available in Java8 and above.
*
* @see <a href=
* "http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888</a>
*/
private static Iterable<Integer> codePointIterator(final String string) {
return new Iterable<Integer>() {
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int nextIndex = 0;
private int length = string.length();
@Override
public boolean hasNext() {
return this.nextIndex < this.length;
}
@Override
public Integer next() {
int result = string.codePointAt(this.nextIndex);
this.nextIndex += Character.charCount(result);
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
/**
* Replace special characters with XML escapes:
*
@@ -71,6 +110,7 @@ public class XML {
* &lt; <small>(less than)</small> is replaced by &amp;lt;
* &gt; <small>(greater than)</small> is replaced by &amp;gt;
* &quot; <small>(double quote)</small> is replaced by &amp;quot;
* &apos; <small>(single quote / apostrophe)</small> is replaced by &amp;apos;
* </pre>
*
* @param string
@@ -79,9 +119,8 @@ public class XML {
*/
public static String escape(String string) {
StringBuilder sb = new StringBuilder(string.length());
for (int i = 0, length = string.length(); i < length; i++) {
char c = string.charAt(i);
switch (c) {
for (final int cp : codePointIterator(string)) {
switch (cp) {
case '&':
sb.append("&amp;");
break;
@@ -98,6 +137,93 @@ public class XML {
sb.append("&apos;");
break;
default:
if (mustEscape(cp)) {
sb.append("&#x");
sb.append(Integer.toHexString(cp));
sb.append(";");
} else {
sb.appendCodePoint(cp);
}
}
}
return sb.toString();
}
/**
* @param cp code point to test
* @return true if the code point is not valid for an XML
*/
private static boolean mustEscape(int cp) {
/* Valid range from https://www.w3.org/TR/REC-xml/#charsets
*
* #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
*
* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
*/
// 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
return (Character.isISOControl(cp)
&& cp != 0x9
&& cp != 0xA
&& cp != 0xD
) || !(
// valid the range of acceptable characters that aren't control
(cp >= 0x20 && cp <= 0xD7FF)
|| (cp >= 0xE000 && cp <= 0xFFFD)
|| (cp >= 0x10000 && cp <= 0x10FFFF)
)
;
}
/**
* Removes XML escapes from the string.
*
* @param string
* string to remove escapes from
* @return string with converted entities
*/
public static String unescape(String string) {
StringBuilder sb = new StringBuilder(string.length());
for (int i = 0, length = string.length(); i < length; i++) {
char c = string.charAt(i);
if (c == '&') {
final int semic = string.indexOf(';', i);
if (semic > i) {
final String entity = string.substring(i + 1, semic);
if (entity.charAt(0) == '#') {
int cp;
if (entity.charAt(1) == 'x') {
// hex encoded unicode
cp = Integer.parseInt(entity.substring(2), 16);
} else {
// decimal encoded unicode
cp = Integer.parseInt(entity.substring(1));
}
sb.appendCodePoint(cp);
} else {
if ("quot".equalsIgnoreCase(entity)) {
sb.append('"');
} else if ("amp".equalsIgnoreCase(entity)) {
sb.append('&');
} else if ("apos".equalsIgnoreCase(entity)) {
sb.append('\'');
} else if ("lt".equalsIgnoreCase(entity)) {
sb.append('<');
} else if ("gt".equalsIgnoreCase(entity)) {
sb.append('>');
} else {
sb.append('&').append(entity).append(';');
}
}
// skip past the entity we just parsed.
i += entity.length() + 1;
} else {
// this shouldn't happen in most cases since the parser
// errors on unclosed enties.
sb.append(c);
}
} else {
// not part of an entity
sb.append(c);
}
}
@@ -227,7 +353,6 @@ public class XML {
if (token == null) {
token = x.nextToken();
}
// attribute = value
if (token instanceof String) {
string = (String) token;
@@ -238,7 +363,7 @@ public class XML {
throw x.syntaxError("Missing value");
}
jsonobject.accumulate(string,
keepStrings ? token : JSONObject.stringToValue((String) token));
keepStrings ? unescape((String)token) : stringToValue((String) token));
token = null;
} else {
jsonobject.accumulate(string, "");
@@ -270,7 +395,7 @@ public class XML {
string = (String) token;
if (string.length() > 0) {
jsonobject.accumulate("content",
keepStrings ? token : JSONObject.stringToValue(string));
keepStrings ? unescape(string) : stringToValue(string));
}
} else if (token == LT) {
@@ -297,16 +422,18 @@ public class XML {
}
/**
* This method has been deprecated in favor of the
* {@link JSONObject.stringToValue(String)} method. Use it instead.
* This method is the same as {@link JSONObject.stringToValue(String)}
* except that this also tries to unescape String values.
*
* @deprecated Use {@link JSONObject#stringToValue(String)} instead.
* @param string String to convert
* @return JSON value of this string or the string
*/
@Deprecated
public static Object stringToValue(String string) {
return JSONObject.stringToValue(string);
Object ret = JSONObject.stringToValue(string);
if(ret instanceof String){
return unescape((String)ret);
}
return ret;
}
/**