50 Commits

Author SHA1 Message Date
Sean Leary
cdf3cf7f81 Update README 2017-10-17 20:05:29 -05:00
Sean Leary
2565abdaaa Merge pull request #362 from johnjaylward/FixXMLUnescape
Fixes XML Unescaping
2017-08-27 10:59:26 -05:00
John J. Aylward
de855c50aa Fixes #361.
* Removes unescape from the XML class calls
* fixes bug with unescape method
* moves unescape logic into the XMLTokener class for more consistency
2017-08-19 18:23:07 -04:00
Sean Leary
4cb1ae802a Merge pull request #360 from migueltt/tokener-fix
Creating a JSONObject from a string that contains a duplicate key (any level) throws a JSONException that includes location
2017-08-17 21:30:01 -05:00
Miguel
2e0a8137bd Removed JSONTokener.back() 2017-08-14 13:01:31 -04:00
Miguel
f177c97258 Replacing tabs with 4-spaces 2017-08-10 19:12:41 -04:00
Miguel
7d8353401a Adding JSONTokener.back() just before throwing JSONException
This forces JSONTokener.syntaxError(..) to point to the last character of the duplicate key.
2017-08-10 19:05:57 -04:00
Miguel
7fed023080 Update to include error location when creating JSONObject from string/text 2017-08-09 21:52:36 -04:00
Sean Leary
d9b8507e6a Merge pull request #354 from johnjaylward/PopulateMapMoreStrict
Updates for populateMap based on discussion in #279 and #264
2017-07-19 18:57:32 -05:00
Sean Leary
d345bc528e Merge pull request #357 from johnjaylward/WriteJavadocUpdate
Update javadoc according to issue #356.
2017-07-15 18:51:18 -05:00
John J. Aylward
6f238a3698 Update javadoc according to issue #356. 2017-07-15 12:17:27 -04:00
John J. Aylward
4dbc5ef803 fixes malformed javadoc 2017-07-09 18:48:40 -04:00
John J. Aylward
5c80c9157d fixes malformed javadoc 2017-07-09 18:47:09 -04:00
John J. Aylward
a129ebe8e4 Adds check for resources opened by our bean mapping 2017-07-09 18:09:14 -04:00
John J. Aylward
641b68dd55 updates javadoc. 2017-07-07 21:33:46 -04:00
John J. Aylward
643b25140f Updates for populateMap based on discussion in #279 and #264 2017-07-07 20:48:42 -04:00
Sean Leary
5024f2d210 Merge pull request #352 from johnjaylward/ErrorMessagePositionFixes
Error message position fixes
2017-07-06 18:07:50 -05:00
John J. Aylward
16baa323cf adds comments 2017-07-03 13:03:02 -04:00
John J. Aylward
52845366bd Fixes more position errors from stepping to new lines and then back. 2017-07-03 13:03:02 -04:00
John J. Aylward
e7e6ed9205 Fixes position reports on errors 2017-06-23 13:40:41 -04:00
Sean Leary
1add1247fa Merge pull request #348 from johnjaylward/ArrayPerformance
Capacity improvements for internal structures
2017-06-12 02:05:26 -05:00
Sean Leary
5b2e5e7579 Merge pull request #347 from ttulka/master
a comment added to explain the use of HashMap
2017-06-11 23:32:06 -05:00
Sean Leary
c9ae1f17d7 Merge pull request #345 from johnjaylward/BetterErrorHandling
Adds JSONException for write value errors
2017-06-11 14:12:48 -05:00
Tomas Tulka
246350bbcd comment added to explain the reason that JSON object is unordered
to avoid implementators' misconceptions and tries to reimplement the
JSON object to keep the elements order
2017-06-09 09:00:17 +02:00
John J. Aylward
2fbe4d96cf change JSONObject(Map) constructor to use the default capacity when a null map is passed 2017-06-08 12:18:04 -04:00
John J. Aylward
3645f91b55 change JSONArray(Collection) constructor to use the default capacity when a null collection is passed 2017-06-08 12:15:03 -04:00
John J. Aylward
9c092753b0 * Updates array constructor and bulk operations to best guess capacity information
* Update JSONObject to allow best guess for initial capacity.
2017-06-08 11:22:23 -04:00
Tomas Tulka
d0f5607998 a comment added to explain the use of HashMap
to avoid misconception of contributors about using HashMap to implement
a JSON object as a unordered collection by the definition
2017-06-08 08:03:14 +02:00
John J. Aylward
ad6bdd715d Adds JSONException for write value errors so serialization errors can be tracked easier 2017-06-05 20:51:57 -04:00
Sean Leary
ef7a5e40be Merge pull request #341 from johnjaylward/OptimizeLoops
Sorry for the late merge, somehow lost track of this pull request.
2017-05-31 20:51:20 -05:00
John J. Aylward
237bf0adb6 more comments 2017-05-31 18:31:02 -04:00
John J. Aylward
f76fbe7005 fixes comments 2017-05-31 18:13:40 -04:00
John J. Aylward
4f5bf16676 * Adds protected entrySet accessor to JSONObject
* Updates loops that request key/value pairs to use the new entrySet accessor
2017-05-23 12:48:44 -04:00
Sean Leary
fbd2be7431 Merge pull request #337 from johnjaylward/OptimizeOpt
Optimizes opt* functions
2017-05-22 22:59:04 -05:00
John J. Aylward
757b6edb03 Merge branch 'master' of github.com:stleary/JSON-java into OptimizeOpt 2017-05-21 13:12:24 -04:00
Sean Leary
f2b642a1ca Merge pull request #336 from johnjaylward/fixSpelling
Numeric enhancements, Refactoring, Fix spelling
2017-05-20 12:37:31 -05:00
John J. Aylward
04d6e83fc2 * Missed JSONArray optFloat and optDouble for the revert
* prevents erasure of stack trace for rethrown exceptions
2017-05-19 09:49:22 -04:00
John J. Aylward
849b392c01 updates the getNumber/optNumber to not return invalid Doubles 2017-05-18 19:49:50 -04:00
John J. Aylward
a7f8ff24df correct string check for JSONObject optBigDecimal and optBigInteger 2017-05-18 14:41:42 -04:00
John J. Aylward
1ab5260a7a * Adds methods getNUmber and getFloat to JSONArray and JSONObject
* Extracts the stringToNumber logic that the optNumber method uses to reuse it between classes
* Fixes -0 issue with optNumber/getNumber
2017-05-18 14:24:34 -04:00
John J. Aylward
c28a2bdf39 * reverts changes to getDouble and related optDouble and optFloat
* Updates optNumber to be smarter about which object it uses to parse strings
2017-05-18 13:07:32 -04:00
John J. Aylward
382f62e781 * Prevent exceptions in cases where the value is not a string.
* Don't call toString when we know it's a string, just cast
2017-05-18 11:41:51 -04:00
John J. Aylward
0c7bd725a6 fixes for javadoc 2017-05-17 11:34:37 -04:00
John J. Aylward
fcdb8671b2 grr, forgot to save changes on last commit 2017-05-17 11:32:44 -04:00
John J. Aylward
c46774cf13 * Update opt* methods for JSONArray
* Add support to JSONArray and JSONObject to optionally get raw number values
* Add support to JSONArray and JSONObject to optionally get float values
2017-05-17 11:29:26 -04:00
John J. Aylward
bd4b180f4e Support for float to BigDecimal in optBigDecimal 2017-05-17 10:51:06 -04:00
John J. Aylward
a8d4e4734f adjustments to opt methods in reference to https://github.com/stleary/JSON-java/issues/334 2017-05-16 19:38:01 -04:00
John J. Aylward
4865f51dd5 change float double literals to be more standard as 1.0f and 1.0d respectively 2017-05-16 15:38:54 -04:00
John J. Aylward
c870094f69 Fixes spelling in comments and removes compile time warnings 2017-05-16 15:35:05 -04:00
John J. Aylward
ae1e9e2b6a fix spelling in javadoc comment 2017-04-03 11:59:36 -04:00
14 changed files with 1118 additions and 386 deletions

View File

@@ -22,7 +22,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
/** /**
* This provides static methods to convert comma delimited text into a * This provides static methods to convert comma delimited text into a
@@ -70,9 +70,12 @@ public class CDL {
c = x.next(); c = x.next();
if (c == q) { if (c == q) {
//Handle escaped double-quote //Handle escaped double-quote
if(x.next() != '\"') char nextC = x.next();
{ if(nextC != '\"') {
x.back(); // if our quote was the end of the file, don't step
if(nextC > 0) {
x.back();
}
break; break;
} }
} }

View File

@@ -1,5 +1,7 @@
package org.json; package org.json;
import java.util.Map.Entry;
/* /*
Copyright (c) 2002 JSON.org Copyright (c) 2002 JSON.org
@@ -24,8 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.util.Iterator;
/** /**
* Convert a web browser cookie list string to a JSONObject and back. * Convert a web browser cookie list string to a JSONObject and back.
* @author JSON.org * @author JSON.org
@@ -39,7 +39,7 @@ public class CookieList {
* The pairs are separated by ';'. The names and the values * The pairs are separated by ';'. The names and the values
* will be unescaped, possibly converting '+' and '%' sequences. * will be unescaped, possibly converting '+' and '%' sequences.
* *
* To add a cookie to a cooklist, * To add a cookie to a cookie list,
* cookielistJSONObject.put(cookieJSONObject.getString("name"), * cookielistJSONObject.put(cookieJSONObject.getString("name"),
* cookieJSONObject.getString("value")); * cookieJSONObject.getString("value"));
* @param string A cookie list string * @param string A cookie list string
@@ -69,18 +69,17 @@ public class CookieList {
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
boolean b = false; boolean b = false;
Iterator<String> keys = jo.keys();
String string;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
while (keys.hasNext()) { for (final Entry<String,?> entry : jo.entrySet()) {
string = keys.next(); final String key = entry.getKey();
if (!jo.isNull(string)) { final Object value = entry.getValue();
if (!JSONObject.NULL.equals(value)) {
if (b) { if (b) {
sb.append(';'); sb.append(';');
} }
sb.append(Cookie.escape(string)); sb.append(Cookie.escape(key));
sb.append("="); sb.append("=");
sb.append(Cookie.escape(jo.getString(string))); sb.append(Cookie.escape(value.toString()));
b = true; b = true;
} }
} }

View File

@@ -24,8 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map.Entry;
/** /**
* Convert an HTTP header to a JSONObject and back. * Convert an HTTP header to a JSONObject and back.
@@ -126,8 +126,6 @@ public class HTTP {
* information. * information.
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
Iterator<String> keys = jo.keys();
String string;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (jo.has("Status-Code") && jo.has("Reason-Phrase")) { if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
sb.append(jo.getString("HTTP-Version")); sb.append(jo.getString("HTTP-Version"));
@@ -147,14 +145,14 @@ public class HTTP {
throw new JSONException("Not enough material for an HTTP header."); throw new JSONException("Not enough material for an HTTP header.");
} }
sb.append(CRLF); sb.append(CRLF);
while (keys.hasNext()) { for (final Entry<String,?> entry : jo.entrySet()) {
string = keys.next(); final String key = entry.getKey();
if (!"HTTP-Version".equals(string) && !"Status-Code".equals(string) && if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) &&
!"Reason-Phrase".equals(string) && !"Method".equals(string) && !"Reason-Phrase".equals(key) && !"Method".equals(key) &&
!"Request-URI".equals(string) && !jo.isNull(string)) { !"Request-URI".equals(key) && !JSONObject.NULL.equals(entry.getValue())) {
sb.append(string); sb.append(key);
sb.append(": "); sb.append(": ");
sb.append(jo.getString(string)); sb.append(jo.optString(key));
sb.append(CRLF); sb.append(CRLF);
} }
} }

View File

@@ -154,8 +154,10 @@ public class JSONArray implements Iterable<Object> {
* A Collection. * A Collection.
*/ */
public JSONArray(Collection<?> collection) { public JSONArray(Collection<?> collection) {
this.myArrayList = new ArrayList<Object>(); if (collection == null) {
if (collection != null) { this.myArrayList = new ArrayList<Object>();
} else {
this.myArrayList = new ArrayList<Object>(collection.size());
for (Object o: collection){ for (Object o: collection){
this.myArrayList.add(JSONObject.wrap(o)); this.myArrayList.add(JSONObject.wrap(o));
} }
@@ -172,6 +174,7 @@ public class JSONArray implements Iterable<Object> {
this(); this();
if (array.getClass().isArray()) { if (array.getClass().isArray()) {
int length = Array.getLength(array); int length = Array.getLength(array);
this.myArrayList.ensureCapacity(length);
for (int i = 0; i < length; i += 1) { for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i))); this.put(JSONObject.wrap(Array.get(array, i)));
} }
@@ -183,7 +186,7 @@ public class JSONArray implements Iterable<Object> {
@Override @Override
public Iterator<Object> iterator() { public Iterator<Object> iterator() {
return myArrayList.iterator(); return this.myArrayList.iterator();
} }
/** /**
@@ -244,7 +247,50 @@ public class JSONArray implements Iterable<Object> {
return object instanceof Number ? ((Number) object).doubleValue() return object instanceof Number ? ((Number) object).doubleValue()
: Double.parseDouble((String) object); : Double.parseDouble((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number."); throw new JSONException("JSONArray[" + index + "] is not a number.", e);
}
}
/**
* Get the float value associated with a key.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The numeric value.
* @throws JSONException
* if the key is not found or if the value is not a Number
* object and cannot be converted to a number.
*/
public float getFloat(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number ? ((Number) object).floatValue()
: Float.parseFloat(object.toString());
} catch (Exception e) {
throw new JSONException("JSONArray[" + index
+ "] is not a number.", e);
}
}
/**
* Get the Number value associated with a key.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The numeric value.
* @throws JSONException
* if the key is not found or if the value is not a Number
* object and cannot be converted to a number.
*/
public Number getNumber(int index) throws JSONException {
Object object = this.get(index);
try {
if (object instanceof Number) {
return (Number)object;
}
return JSONObject.stringToNumber(object.toString());
} catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.", e);
} }
} }
@@ -266,9 +312,8 @@ public class JSONArray implements Iterable<Object> {
// 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[" + JSONObject.quote(Integer.toString(index)) throw new JSONException("JSONArray[" + index + "] is not an enum of type "
+ "] is not an enum of type " + JSONObject.quote(clazz.getSimpleName()) + JSONObject.quote(clazz.getSimpleName()) + ".");
+ ".");
} }
return val; return val;
} }
@@ -289,7 +334,7 @@ public class JSONArray implements Iterable<Object> {
return new BigDecimal(object.toString()); return new BigDecimal(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + throw new JSONException("JSONArray[" + index +
"] could not convert to BigDecimal."); "] could not convert to BigDecimal.", e);
} }
} }
@@ -309,7 +354,7 @@ public class JSONArray implements Iterable<Object> {
return new BigInteger(object.toString()); return new BigInteger(object.toString());
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + throw new JSONException("JSONArray[" + index +
"] could not convert to BigInteger."); "] could not convert to BigInteger.", e);
} }
} }
@@ -328,7 +373,7 @@ public class JSONArray implements Iterable<Object> {
return object instanceof Number ? ((Number) object).intValue() return object instanceof Number ? ((Number) object).intValue()
: Integer.parseInt((String) object); : Integer.parseInt((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number."); throw new JSONException("JSONArray[" + index + "] is not a number.", e);
} }
} }
@@ -384,7 +429,7 @@ public class JSONArray implements Iterable<Object> {
return object instanceof Number ? ((Number) object).longValue() return object instanceof Number ? ((Number) object).longValue()
: Long.parseLong((String) object); : Long.parseLong((String) object);
} catch (Exception e) { } catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number."); throw new JSONException("JSONArray[" + index + "] is not a number.", e);
} }
} }
@@ -453,7 +498,7 @@ public class JSONArray implements Iterable<Object> {
* Get the optional object value associated with an index. * Get the optional object value associated with an index.
* *
* @param index * @param index
* The index must be between 0 and length() - 1. * The index must be between 0 and length() - 1. If not, null is returned.
* @return An object value, or null if there is no object at that index. * @return An object value, or null if there is no object at that index.
*/ */
public Object opt(int index) { public Object opt(int index) {
@@ -518,11 +563,63 @@ 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) {
try { Object val = this.opt(index);
return this.getDouble(index); if (JSONObject.NULL.equals(val)) {
} catch (Exception e) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){
return ((Number) val).doubleValue();
}
if (val instanceof String) {
try {
return Double.parseDouble((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
}
/**
* Get the optional float value associated with an index. NaN 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.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The value.
*/
public float optFloat(int index) {
return this.optFloat(index, Float.NaN);
}
/**
* Get the optional float value associated with an index. 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.
*
* @param index
* subscript
* @param defaultValue
* The default value.
* @return The value.
*/
public float optFloat(int index, float defaultValue) {
Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) {
return defaultValue;
}
if (val instanceof Number){
return ((Number) val).floatValue();
}
if (val instanceof String) {
try {
return Float.parseFloat((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -550,11 +647,22 @@ 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) {
try { Object val = this.opt(index);
return this.getInt(index); if (JSONObject.NULL.equals(val)) {
} catch (Exception e) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){
return ((Number) val).intValue();
}
if (val instanceof String) {
try {
return new BigDecimal(val.toString()).intValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
@@ -615,8 +723,29 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public BigInteger optBigInteger(int index, BigInteger defaultValue) { public BigInteger optBigInteger(int index, BigInteger defaultValue) {
Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) {
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 { try {
return this.getBigInteger(index); final String valStr = val.toString();
if(JSONObject.isDecimalNotation(valStr)) {
return new BigDecimal(valStr).toBigInteger();
}
return new BigInteger(valStr);
} catch (Exception e) { } catch (Exception e) {
return defaultValue; return defaultValue;
} }
@@ -634,8 +763,25 @@ public class JSONArray implements Iterable<Object> {
* @return The value. * @return The value.
*/ */
public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) {
Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) {
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 { try {
return this.getBigDecimal(index); return new BigDecimal(val.toString());
} catch (Exception e) { } catch (Exception e) {
return defaultValue; return defaultValue;
} }
@@ -693,17 +839,73 @@ 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) {
try { Object val = this.opt(index);
return this.getLong(index); if (JSONObject.NULL.equals(val)) {
} catch (Exception e) {
return defaultValue; return defaultValue;
} }
if (val instanceof Number){
return ((Number) val).longValue();
}
if (val instanceof String) {
try {
return new BigDecimal(val.toString()).longValue();
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
}
/**
* Get an optional {@link Number} value associated with a key, or <code>null</code>
* if there is no such key or if the value is not a number. If the value is a string,
* an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method
* would be used in cases where type coercion of the number value is unwanted.
*
* @param index
* The index must be between 0 and length() - 1.
* @return An object which is the value.
*/
public Number optNumber(int index) {
return this.optNumber(index, null);
}
/**
* Get an optional {@link Number} value associated with a key, or the default if there
* is no such key or if the value is not a number. If the value is a string,
* an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method
* would be used in cases where type coercion of the number value is unwanted.
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default.
* @return An object which is the value.
*/
public Number optNumber(int index, Number defaultValue) {
Object val = this.opt(index);
if (JSONObject.NULL.equals(val)) {
return defaultValue;
}
if (val instanceof Number){
return (Number) val;
}
if (val instanceof String) {
try {
return JSONObject.stringToNumber((String) val);
} catch (Exception e) {
return defaultValue;
}
}
return defaultValue;
} }
/** /**
* Get the optional string value associated with an index. It returns an * Get the optional string value associated with an index. It returns an
* empty string if there is no value at that index. If the value is not a * empty string if there is no value at that index. If the value is not a
* string and is not null, then it is coverted to a string. * string and is not null, then it is converted to a string.
* *
* @param index * @param index
* The index must be between 0 and length() - 1. * The index must be between 0 and length() - 1.
@@ -951,7 +1153,13 @@ public class JSONArray implements Iterable<Object> {
} }
if (index < this.length()) { if (index < this.length()) {
this.myArrayList.set(index, value); this.myArrayList.set(index, value);
} else if(index == this.length()){
// simple append
this.put(value);
} else { } else {
// if we are inserting past the length, we want to grow the array all at once
// instead of incrementally.
this.myArrayList.ensureCapacity(index + 1);
while (index != this.length()) { while (index != this.length()) {
this.put(JSONObject.NULL); this.put(JSONObject.NULL);
} }
@@ -1022,7 +1230,7 @@ public class JSONArray implements Iterable<Object> {
* Queries and returns a value from this object using {@code jsonPointer}, or * Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key. * returns null if the query fails due to a missing key.
* *
* @param The JSON pointer * @param jsonPointer The JSON pointer
* @return the queried value or {@code null} * @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/ */
@@ -1064,8 +1272,14 @@ public class JSONArray implements Iterable<Object> {
return false; return false;
} }
for (int i = 0; i < len; i += 1) { for (int i = 0; i < len; i += 1) {
Object valueThis = this.get(i); Object valueThis = this.myArrayList.get(i);
Object valueOther = ((JSONArray)other).get(i); Object valueOther = ((JSONArray)other).myArrayList.get(i);
if(valueThis == valueOther) {
return true;
}
if(valueThis == null) {
return false;
}
if (valueThis instanceof JSONObject) { if (valueThis instanceof JSONObject) {
if (!((JSONObject)valueThis).similar(valueOther)) { if (!((JSONObject)valueThis).similar(valueOther)) {
return false; return false;
@@ -1097,7 +1311,7 @@ public class JSONArray implements Iterable<Object> {
if (names == null || names.length() == 0 || this.length() == 0) { if (names == null || names.length() == 0 || this.length() == 0) {
return null; return null;
} }
JSONObject jo = new JSONObject(); JSONObject jo = new JSONObject(names.length());
for (int i = 0; i < names.length(); i += 1) { for (int i = 0; i < names.length(); i += 1) {
jo.put(names.getString(i), this.opt(i)); jo.put(names.getString(i), this.opt(i));
} }
@@ -1109,12 +1323,14 @@ public class JSONArray implements Iterable<Object> {
* whitespace is added. If it is not possible to produce a syntactically * whitespace is added. If it is not possible to produce a syntactically
* correct JSON text then null will be returned instead. This could occur if * correct JSON text then null will be returned instead. This could occur if
* the array contains an invalid number. * the array contains an invalid number.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @return a printable, displayable, transmittable representation of the * @return a printable, displayable, transmittable representation of the
* array. * array.
*/ */
@Override
public String toString() { public String toString() {
try { try {
return this.toString(0); return this.toString(0);
@@ -1124,9 +1340,24 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method * Make a pretty-printed JSON text of this JSONArray.
* assumes that the data structure is acyclical. *
* * <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
* one element, then the array will be output on a single line:
* <pre>{@code [1]}</pre>
*
* <p>If an array has 2 or more elements, then it will be output across
* multiple lines: <pre>{@code
* [
* 1,
* "value 2",
* 3
* ]
* }</pre>
* <p><b>
* Warning: This method assumes that the data structure is acyclical.
* </b>
*
* @param indentFactor * @param indentFactor
* The number of spaces to add to each level of indentation. * The number of spaces to add to each level of indentation.
* @return a printable, displayable, transmittable representation of the * @return a printable, displayable, transmittable representation of the
@@ -1145,8 +1376,9 @@ public class JSONArray implements Iterable<Object> {
/** /**
* Write the contents of the JSONArray as JSON text to a writer. For * Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added. * compactness, no whitespace is added.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
*</b>
* *
* @return The writer. * @return The writer.
* @throws JSONException * @throws JSONException
@@ -1156,17 +1388,30 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Write the contents of the JSONArray as JSON text to a writer. For * Write the contents of the JSONArray as JSON text to a writer.
* compactness, no whitespace is added. *
* <p> * <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
* one element, then the array will be output on a single line:
* <pre>{@code [1]}</pre>
*
* <p>If an array has 2 or more elements, then it will be output across
* multiple lines: <pre>{@code
* [
* 1,
* "value 2",
* 3
* ]
* }</pre>
* <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @param writer * @param writer
* Writes the serialized JSON * Writes the serialized JSON
* @param indentFactor * @param indentFactor
* The number of spaces to add to each level of indentation. * The number of spaces to add to each level of indentation.
* @param indent * @param indent
* The indention of the top level. * The indentation of the top level.
* @return The writer. * @return The writer.
* @throws JSONException * @throws JSONException
*/ */
@@ -1178,8 +1423,12 @@ public class JSONArray implements Iterable<Object> {
writer.write('['); writer.write('[');
if (length == 1) { if (length == 1) {
JSONObject.writeValue(writer, this.myArrayList.get(0), try {
indentFactor, indent); JSONObject.writeValue(writer, this.myArrayList.get(0),
indentFactor, indent);
} catch (Exception 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;
@@ -1191,8 +1440,12 @@ public class JSONArray implements Iterable<Object> {
writer.write('\n'); writer.write('\n');
} }
JSONObject.indent(writer, newindent); JSONObject.indent(writer, newindent);
JSONObject.writeValue(writer, this.myArrayList.get(i), try {
indentFactor, newindent); JSONObject.writeValue(writer, this.myArrayList.get(i),
indentFactor, newindent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONArray value at index: " + i, e);
}
commanate = true; commanate = true;
} }
if (indentFactor > 0) { if (indentFactor > 0) {

View File

@@ -1,5 +1,7 @@
package org.json; package org.json;
import java.util.Map.Entry;
/* /*
Copyright (c) 2008 JSON.org Copyright (c) 2008 JSON.org
@@ -24,9 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.util.Iterator;
/** /**
* This provides static methods to convert an XML text into a JSONArray or * This provides static methods to convert an XML text into a JSONArray or
* JSONObject, and to covert a JSONArray or JSONObject into an XML text using * JSONObject, and to covert a JSONArray or JSONObject into an XML text using
@@ -42,7 +41,7 @@ public class JSONML {
* @param arrayForm true if array form, false if object form. * @param arrayForm true if array form, false if object form.
* @param ja The JSONArray that is containing the current tag or null * @param ja The JSONArray that is containing the current tag or null
* if we are at the outermost level. * if we are at the outermost level.
* @param keepStrings Don't type-convert text nodes and attibute values * @param keepStrings Don't type-convert text nodes and attribute values
* @return A JSONArray if the value is the outermost tag, otherwise null. * @return A JSONArray if the value is the outermost tag, otherwise null.
* @throws JSONException * @throws JSONException
*/ */
@@ -175,7 +174,7 @@ public class JSONML {
if (!(token instanceof String)) { if (!(token instanceof String)) {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
newjo.accumulate(attribute, keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)); newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token));
token = null; token = null;
} else { } else {
newjo.accumulate(attribute, ""); newjo.accumulate(attribute, "");
@@ -397,13 +396,10 @@ public class JSONML {
public static String toString(JSONArray ja) throws JSONException { public static String toString(JSONArray ja) throws JSONException {
int i; int i;
JSONObject jo; JSONObject jo;
String key;
Iterator<String> keys;
int length; int length;
Object object; Object object;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
String tagName; String tagName;
String value;
// Emit <tagName // Emit <tagName
@@ -420,17 +416,16 @@ public class JSONML {
// Emit the attributes // Emit the attributes
keys = jo.keys(); for (final Entry<String, ?> entry : jo.entrySet()) {
while (keys.hasNext()) { final String key = entry.getKey();
key = keys.next();
XML.noSpace(key); XML.noSpace(key);
value = jo.optString(key); final Object value = entry.getValue();
if (value != null) { if (value != null) {
sb.append(' '); sb.append(' ');
sb.append(XML.escape(key)); sb.append(XML.escape(key));
sb.append('='); sb.append('=');
sb.append('"'); sb.append('"');
sb.append(XML.escape(value)); sb.append(XML.escape(value.toString()));
sb.append('"'); sb.append('"');
} }
} }
@@ -482,12 +477,10 @@ public class JSONML {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int i; int i;
JSONArray ja; JSONArray ja;
String key;
Iterator<String> keys;
int length; int length;
Object object; Object object;
String tagName; String tagName;
String value; Object value;
//Emit <tagName //Emit <tagName
@@ -502,18 +495,17 @@ public class JSONML {
//Emit the attributes //Emit the attributes
keys = jo.keys(); for (final Entry<String, ?> entry : jo.entrySet()) {
while (keys.hasNext()) { final String key = entry.getKey();
key = keys.next();
if (!"tagName".equals(key) && !"childNodes".equals(key)) { if (!"tagName".equals(key) && !"childNodes".equals(key)) {
XML.noSpace(key); XML.noSpace(key);
value = jo.optString(key); value = entry.getValue();
if (value != null) { if (value != null) {
sb.append(' '); sb.append(' ');
sb.append(XML.escape(key)); sb.append(XML.escape(key));
sb.append('='); sb.append('=');
sb.append('"'); sb.append('"');
sb.append(XML.escape(value)); sb.append(XML.escape(value.toString()));
sb.append('"'); sb.append('"');
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -68,11 +68,11 @@ public class JSONPointer {
* {@link #append(String)} method calls. * {@link #append(String)} method calls.
*/ */
public JSONPointer build() { public JSONPointer build() {
return new JSONPointer(refTokens); return new JSONPointer(this.refTokens);
} }
/** /**
* Adds an arbitary token to the list of reference tokens. It can be any non-null value. * Adds an arbitrary token to the list of reference tokens. It can be any non-null value.
* *
* Unlike in the case of JSON string or URI fragment representation of JSON pointers, the * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the
* argument of this method MUST NOT be escaped. If you want to query the property called * argument of this method MUST NOT be escaped. If you want to query the property called
@@ -87,7 +87,7 @@ public class JSONPointer {
if (token == null) { if (token == null) {
throw new NullPointerException("token cannot be null"); throw new NullPointerException("token cannot be null");
} }
refTokens.add(token); this.refTokens.add(token);
return this; return this;
} }
@@ -99,7 +99,7 @@ public class JSONPointer {
* @return {@code this} * @return {@code this}
*/ */
public Builder append(int arrayIndex) { public Builder append(int arrayIndex) {
refTokens.add(String.valueOf(arrayIndex)); this.refTokens.add(String.valueOf(arrayIndex));
return this; return this;
} }
} }
@@ -134,29 +134,30 @@ public class JSONPointer {
* @param pointer the JSON String or URI Fragment representation of the JSON pointer. * @param pointer the JSON String or URI Fragment representation of the JSON pointer.
* @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer
*/ */
public JSONPointer(String pointer) { public JSONPointer(final String pointer) {
if (pointer == null) { if (pointer == null) {
throw new NullPointerException("pointer cannot be null"); throw new NullPointerException("pointer cannot be null");
} }
if (pointer.isEmpty() || pointer.equals("#")) { if (pointer.isEmpty() || pointer.equals("#")) {
refTokens = Collections.emptyList(); this.refTokens = Collections.emptyList();
return; return;
} }
String refs;
if (pointer.startsWith("#/")) { if (pointer.startsWith("#/")) {
pointer = pointer.substring(2); refs = pointer.substring(2);
try { try {
pointer = URLDecoder.decode(pointer, ENCODING); refs = URLDecoder.decode(refs, ENCODING);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} else if (pointer.startsWith("/")) { } else if (pointer.startsWith("/")) {
pointer = pointer.substring(1); refs = pointer.substring(1);
} else { } else {
throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
} }
refTokens = new ArrayList<String>(); this.refTokens = new ArrayList<String>();
for (String token : pointer.split("/")) { for (String token : refs.split("/")) {
refTokens.add(unescape(token)); this.refTokens.add(unescape(token));
} }
} }
@@ -181,11 +182,11 @@ public class JSONPointer {
* @throws JSONPointerException if an error occurs during evaluation * @throws JSONPointerException if an error occurs during evaluation
*/ */
public Object queryFrom(Object document) { public Object queryFrom(Object document) {
if (refTokens.isEmpty()) { if (this.refTokens.isEmpty()) {
return document; return document;
} }
Object current = document; Object current = document;
for (String token : refTokens) { for (String token : this.refTokens) {
if (current instanceof JSONObject) { if (current instanceof JSONObject) {
current = ((JSONObject) current).opt(unescape(token)); current = ((JSONObject) current).opt(unescape(token));
} else if (current instanceof JSONArray) { } else if (current instanceof JSONArray) {
@@ -206,6 +207,7 @@ 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
* JSONPointerException is thrown * JSONPointerException is thrown
*/ */
@SuppressWarnings("boxing")
private Object readByIndexToken(Object current, String indexToken) { private Object readByIndexToken(Object current, String indexToken) {
try { try {
int index = Integer.parseInt(indexToken); int index = Integer.parseInt(indexToken);
@@ -227,7 +229,7 @@ public class JSONPointer {
@Override @Override
public String toString() { public String toString() {
StringBuilder rval = new StringBuilder(""); StringBuilder rval = new StringBuilder("");
for (String token: refTokens) { for (String token: this.refTokens) {
rval.append('/').append(escape(token)); rval.append('/').append(escape(token));
} }
return rval.toString(); return rval.toString();
@@ -255,7 +257,7 @@ public class JSONPointer {
public String toURIFragment() { public String toURIFragment() {
try { try {
StringBuilder rval = new StringBuilder("#"); StringBuilder rval = new StringBuilder("#");
for (String token : refTokens) { for (String token : this.refTokens) {
rval.append('/').append(URLEncoder.encode(token, ENCODING)); rval.append('/').append(URLEncoder.encode(token, ENCODING));
} }
return rval.toString(); return rval.toString();

View File

@@ -72,6 +72,7 @@ public class JSONStringer extends JSONWriter {
* <code>endArray</code>). * <code>endArray</code>).
* @return The JSON text. * @return The JSON text.
*/ */
@Override
public String toString() { public String toString() {
return this.mode == 'd' ? this.writer.toString() : null; return this.mode == 'd' ? this.writer.toString() : null;
} }

View File

@@ -29,7 +29,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
/** /**
* A JSONTokener takes a source string and extracts characters and tokens from * A JSONTokener takes a source string and extracts characters and tokens from
@@ -39,36 +39,45 @@ SOFTWARE.
* @version 2014-05-03 * @version 2014-05-03
*/ */
public class JSONTokener { public class JSONTokener {
/** current read character position on the current line. */
private long character; private long character;
/** flag to indicate if the end of the input has been found. */
private boolean eof; private boolean eof;
private long index; /** current read index of the input. */
private long line; private long index;
private char previous; /** current line of the input. */
private Reader reader; private long line;
/** previous character read from the input. */
private char previous;
/** Reader for the input. */
private final Reader reader;
/** flag to indicate that a previous character was requested. */
private boolean usePrevious; private boolean usePrevious;
/** the number of characters read in the previous line. */
private long characterPreviousLine;
/** /**
* Construct a JSONTokener from a Reader. * Construct a JSONTokener from a Reader. The caller must close the Reader.
* *
* @param reader A reader. * @param reader A reader.
*/ */
public JSONTokener(Reader reader) { public JSONTokener(Reader reader) {
this.reader = reader.markSupported() this.reader = reader.markSupported()
? reader ? reader
: new BufferedReader(reader); : new BufferedReader(reader);
this.eof = false; this.eof = false;
this.usePrevious = false; this.usePrevious = false;
this.previous = 0; this.previous = 0;
this.index = 0; this.index = 0;
this.character = 1; this.character = 1;
this.characterPreviousLine = 0;
this.line = 1; this.line = 1;
} }
/** /**
* Construct a JSONTokener from an InputStream. * Construct a JSONTokener from an InputStream. The caller must close the input stream.
* @param inputStream The source. * @param inputStream The source.
*/ */
public JSONTokener(InputStream inputStream) { public JSONTokener(InputStream inputStream) {
@@ -97,12 +106,23 @@ public class JSONTokener {
if (this.usePrevious || this.index <= 0) { if (this.usePrevious || this.index <= 0) {
throw new JSONException("Stepping back two steps is not supported"); throw new JSONException("Stepping back two steps is not supported");
} }
this.index -= 1; this.decrementIndexes();
this.character -= 1;
this.usePrevious = true; this.usePrevious = true;
this.eof = false; this.eof = false;
} }
/**
* Decrements the indexes for the {@link #back()} method based on the previous character read.
*/
private void decrementIndexes() {
this.index--;
if(this.previous=='\r' || this.previous == '\n') {
this.line--;
this.character=this.characterPreviousLine ;
} else if(this.character > 0){
this.character--;
}
}
/** /**
* Get the hex value of a character (base16). * Get the hex value of a character (base16).
@@ -124,6 +144,8 @@ public class JSONTokener {
} }
/** /**
* Checks if the end of the input has been reached.
*
* @return true if at the end of the file and we didn't step back * @return true if at the end of the file and we didn't step back
*/ */
public boolean end() { public boolean end() {
@@ -139,11 +161,24 @@ public class JSONTokener {
* or backward while checking for more data. * or backward while checking for more data.
*/ */
public boolean more() throws JSONException { public boolean more() throws JSONException {
this.next(); if(this.usePrevious) {
if (this.end()) { return true;
return false; }
try {
this.reader.mark(1);
} catch (IOException e) {
throw new JSONException("Unable to preserve stream position", e);
}
try {
// -1 is EOF, but next() can not consume the null character '\0'
if(this.reader.read() <= 0) {
this.eof = true;
return false;
}
this.reader.reset();
} catch (IOException e) {
throw new JSONException("Unable to read the next character from the stream", e);
} }
this.back();
return true; return true;
} }
@@ -165,26 +200,39 @@ public class JSONTokener {
} catch (IOException exception) { } catch (IOException exception) {
throw new JSONException(exception); throw new JSONException(exception);
} }
if (c <= 0) { // End of stream
this.eof = true;
c = 0;
}
} }
this.index += 1; if (c <= 0) { // End of stream
if (this.previous == '\r') { this.eof = true;
this.line += 1; return 0;
this.character = c == '\n' ? 0 : 1;
} else if (c == '\n') {
this.line += 1;
this.character = 0;
} else {
this.character += 1;
} }
this.incrementIndexes(c);
this.previous = (char) c; this.previous = (char) c;
return this.previous; return this.previous;
} }
/**
* Increments the internal indexes according to the previous character
* read and the character passed as the current character.
* @param c the current character read.
*/
private void incrementIndexes(int c) {
if(c > 0) {
this.index++;
if(c=='\r') {
this.line++;
this.characterPreviousLine = this.character;
this.character=0;
}else if (c=='\n') {
if(this.previous != '\r') {
this.line++;
this.characterPreviousLine = this.character;
}
this.character=0;
} else {
this.character++;
}
}
}
/** /**
* Consume the next character, and check that it matches a specified * Consume the next character, and check that it matches a specified
@@ -196,8 +244,11 @@ public class JSONTokener {
public char next(char c) throws JSONException { public char next(char c) throws JSONException {
char n = this.next(); char n = this.next();
if (n != c) { if (n != c) {
throw this.syntaxError("Expected '" + c + "' and instead saw '" + if(n > 0) {
n + "'"); throw this.syntaxError("Expected '" + c + "' and instead saw '" +
n + "'");
}
throw this.syntaxError("Expected '" + c + "' and instead saw ''");
} }
return n; return n;
} }
@@ -212,23 +263,23 @@ public class JSONTokener {
* Substring bounds error if there are not * Substring bounds error if there are not
* n characters remaining in the source string. * n characters remaining in the source string.
*/ */
public String next(int n) throws JSONException { public String next(int n) throws JSONException {
if (n == 0) { if (n == 0) {
return ""; return "";
} }
char[] chars = new char[n]; char[] chars = new char[n];
int pos = 0; int pos = 0;
while (pos < n) { while (pos < n) {
chars[pos] = this.next(); chars[pos] = this.next();
if (this.end()) { if (this.end()) {
throw this.syntaxError("Substring bounds error"); throw this.syntaxError("Substring bounds error");
} }
pos += 1; pos += 1;
} }
return new String(chars); return new String(chars);
} }
/** /**
@@ -372,15 +423,15 @@ public class JSONTokener {
String string; String string;
switch (c) { switch (c) {
case '"': case '"':
case '\'': case '\'':
return this.nextString(c); return this.nextString(c);
case '{': case '{':
this.back(); this.back();
return new JSONObject(this); return new JSONObject(this);
case '[': case '[':
this.back(); this.back();
return new JSONArray(this); return new JSONArray(this);
} }
/* /*
@@ -426,13 +477,17 @@ public class JSONTokener {
do { do {
c = this.next(); c = this.next();
if (c == 0) { if (c == 0) {
// in some readers, reset() may throw an exception if
// the remaining portion of the input is greater than
// the mark size (1,000,000 above).
this.reader.reset(); this.reader.reset();
this.index = startIndex; this.index = startIndex;
this.character = startCharacter; this.character = startCharacter;
this.line = startLine; this.line = startLine;
return c; return 0;
} }
} while (c != to); } while (c != to);
this.reader.mark(1);
} catch (IOException exception) { } catch (IOException exception) {
throw new JSONException(exception); throw new JSONException(exception);
} }
@@ -440,7 +495,6 @@ public class JSONTokener {
return c; return c;
} }
/** /**
* Make a JSONException to signal a syntax error. * Make a JSONException to signal a syntax error.
* *
@@ -470,6 +524,6 @@ public class JSONTokener {
@Override @Override
public String toString() { public String toString() {
return " at " + this.index + " [character " + this.character + " line " + return " at " + this.index + " [character " + this.character + " line " +
this.line + "]"; this.line + "]";
} }
} }

View File

@@ -149,18 +149,18 @@ public class JSONWriter {
/** /**
* End something. * End something.
* @param mode Mode * @param m Mode
* @param c Closing character * @param c Closing character
* @return this * @return this
* @throws JSONException If unbalanced. * @throws JSONException If unbalanced.
*/ */
private JSONWriter end(char mode, char c) throws JSONException { private JSONWriter end(char m, char c) throws JSONException {
if (this.mode != mode) { if (this.mode != m) {
throw new JSONException(mode == 'a' throw new JSONException(m == 'a'
? "Misplaced endArray." ? "Misplaced endArray."
: "Misplaced endObject."); : "Misplaced endObject.");
} }
this.pop(mode); this.pop(m);
try { try {
this.writer.append(c); this.writer.append(c);
} catch (IOException e) { } catch (IOException e) {

View File

@@ -25,7 +25,7 @@ SOFTWARE.
*/ */
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator; import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
/** /**
@@ -41,7 +41,7 @@ public class Property {
* @throws JSONException * @throws JSONException
*/ */
public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException { public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException {
JSONObject jo = new JSONObject(); JSONObject jo = new JSONObject(properties == null ? 0 : properties.size());
if (properties != null && !properties.isEmpty()) { if (properties != null && !properties.isEmpty()) {
Enumeration<?> enumProperties = properties.propertyNames(); Enumeration<?> enumProperties = properties.propertyNames();
while(enumProperties.hasMoreElements()) { while(enumProperties.hasMoreElements()) {
@@ -61,10 +61,11 @@ public class Property {
public static Properties toProperties(JSONObject jo) throws JSONException { public static Properties toProperties(JSONObject jo) throws JSONException {
Properties properties = new Properties(); Properties properties = new Properties();
if (jo != null) { if (jo != null) {
Iterator<String> keys = jo.keys(); for (final Entry<String, ?> entry : jo.entrySet()) {
while (keys.hasNext()) { Object value = entry.getValue();
String name = keys.next(); if (!JSONObject.NULL.equals(value)) {
properties.put(name, jo.getString(name)); properties.put(entry.getKey(), value.toString());
}
} }
} }
return properties; return properties;

2
README
View File

@@ -89,6 +89,8 @@ invalid number formats (1.2e6.3) will cause errors as such documents can not be
reliably. reliably.
Release history: Release history:
20171018 Checkpoint for recent commits.
20170516 Roll up recent commits. 20170516 Roll up recent commits.
20160810 Revert code that was breaking opt*() methods. 20160810 Revert code that was breaking opt*() methods.

View File

@@ -25,6 +25,7 @@ SOFTWARE.
*/ */
import java.util.Iterator; import java.util.Iterator;
import java.util.Map.Entry;
/** /**
* 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
@@ -140,7 +141,7 @@ public class XML {
if (mustEscape(cp)) { if (mustEscape(cp)) {
sb.append("&#x"); sb.append("&#x");
sb.append(Integer.toHexString(cp)); sb.append(Integer.toHexString(cp));
sb.append(";"); sb.append(';');
} else { } else {
sb.appendCodePoint(cp); sb.appendCodePoint(cp);
} }
@@ -190,36 +191,12 @@ public class XML {
final int semic = string.indexOf(';', i); final int semic = string.indexOf(';', i);
if (semic > i) { if (semic > i) {
final String entity = string.substring(i + 1, semic); final String entity = string.substring(i + 1, semic);
if (entity.charAt(0) == '#') { sb.append(XMLTokener.unescapeEntity(entity));
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. // skip past the entity we just parsed.
i += entity.length() + 1; i += entity.length() + 1;
} else { } else {
// this shouldn't happen in most cases since the parser // this shouldn't happen in most cases since the parser
// errors on unclosed enties. // errors on unclosed entries.
sb.append(c); sb.append(c);
} }
} else { } else {
@@ -363,7 +340,7 @@ public class XML {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
jsonobject.accumulate(string, jsonobject.accumulate(string,
keepStrings ? unescape((String)token) : stringToValue((String) token)); keepStrings ? ((String)token) : stringToValue((String) token));
token = null; token = null;
} else { } else {
jsonobject.accumulate(string, ""); jsonobject.accumulate(string, "");
@@ -395,7 +372,7 @@ public class XML {
string = (String) token; string = (String) token;
if (string.length() > 0) { if (string.length() > 0) {
jsonobject.accumulate("content", jsonobject.accumulate("content",
keepStrings ? unescape(string) : stringToValue(string)); keepStrings ? string : stringToValue(string));
} }
} else if (token == LT) { } else if (token == LT) {
@@ -422,18 +399,14 @@ public class XML {
} }
/** /**
* This method is the same as {@link JSONObject.stringToValue(String)} * This method is the same as {@link JSONObject#stringToValue(String)}
* except that this also tries to unescape String values. * except that this also tries to unescape String values.
* *
* @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
*/ */
public static Object stringToValue(String string) { public static Object stringToValue(String string) {
Object ret = JSONObject.stringToValue(string); return JSONObject.stringToValue(string);
if(ret instanceof String){
return unescape((String)ret);
}
return ret;
} }
/** /**
@@ -508,15 +481,12 @@ 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(Object object, String tagName) public static String toString(final Object object, final String tagName)
throws JSONException { throws JSONException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
JSONArray ja; JSONArray ja;
JSONObject jo; JSONObject jo;
String key;
Iterator<String> keys;
String string; String string;
Object value;
if (object instanceof JSONObject) { if (object instanceof JSONObject) {
@@ -529,16 +499,14 @@ public class XML {
// Loop thru the keys. // Loop thru the keys.
jo = (JSONObject) object; jo = (JSONObject) object;
keys = jo.keys(); for (final Entry<String, ?> entry : jo.entrySet()) {
while (keys.hasNext()) { final String key = entry.getKey();
key = keys.next(); Object value = entry.getValue();
value = jo.opt(key);
if (value == null) { if (value == null) {
value = ""; value = "";
} else if (value.getClass().isArray()) { } else if (value.getClass().isArray()) {
value = new JSONArray(value); value = new JSONArray(value);
} }
string = value instanceof String ? (String) value : null;
// Emit content in body // Emit content in body
if ("content".equals(key)) { if ("content".equals(key)) {
@@ -595,21 +563,19 @@ public class XML {
} }
if (object != null) { if (object != null && (object instanceof JSONArray || object.getClass().isArray())) {
if (object.getClass().isArray()) { if(object.getClass().isArray()) {
object = new JSONArray(object); ja = new JSONArray(object);
} } else {
if (object instanceof JSONArray) {
ja = (JSONArray) object; ja = (JSONArray) object;
for (Object val : ja) {
// XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an
// <array> element.
sb.append(toString(val, tagName == null ? "array" : tagName));
}
return sb.toString();
} }
for (Object val : ja) {
// XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an
// <array> element.
sb.append(toString(val, tagName == null ? "array" : tagName));
}
return sb.toString();
} }
string = (object == null) ? "null" : escape(object.toString()); string = (object == null) ? "null" : escape(object.toString());

View File

@@ -64,11 +64,8 @@ public class XMLTokener extends JSONTokener {
char c; char c;
int i; int i;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (;;) { while (more()) {
c = next(); c = next();
if (end()) {
throw syntaxError("Unclosed CDATA");
}
sb.append(c); sb.append(c);
i = sb.length() - 3; i = sb.length() - 3;
if (i >= 0 && sb.charAt(i) == ']' && if (i >= 0 && sb.charAt(i) == ']' &&
@@ -77,6 +74,7 @@ public class XMLTokener extends JSONTokener {
return sb.toString(); return sb.toString();
} }
} }
throw syntaxError("Unclosed CDATA");
} }
@@ -103,7 +101,10 @@ public class XMLTokener extends JSONTokener {
} }
sb = new StringBuilder(); sb = new StringBuilder();
for (;;) { for (;;) {
if (c == '<' || c == 0) { if (c == 0) {
return sb.toString().trim();
}
if (c == '<') {
back(); back();
return sb.toString().trim(); return sb.toString().trim();
} }
@@ -137,8 +138,37 @@ public class XMLTokener extends JSONTokener {
} }
} }
String string = sb.toString(); String string = sb.toString();
Object object = entity.get(string); return unescapeEntity(string);
return object != null ? object : ampersand + string + ";"; }
/**
* Unescapes an XML entity encoding;
* @param e entity (only the actual entity value, not the preceding & or ending ;
* @return
*/
static String unescapeEntity(String e) {
// validate
if (e == null || e.isEmpty()) {
return "";
}
// if our entity is an encoded unicode point, parse it.
if (e.charAt(0) == '#') {
int cp;
if (e.charAt(1) == 'x') {
// hex encoded unicode
cp = Integer.parseInt(e.substring(2), 16);
} else {
// decimal encoded unicode
cp = Integer.parseInt(e.substring(1));
}
return new String(new int[] {cp},0,1);
}
Character knownEntity = entity.get(e);
if(knownEntity==null) {
// we don't know the entity so keep it encoded
return '&' + e + ';';
}
return knownEntity.toString();
} }