diff --git a/CookieList.java b/CookieList.java index 8cb4e5e..c67ee3a 100644 --- a/CookieList.java +++ b/CookieList.java @@ -1,7 +1,5 @@ package org.json; -import java.util.Map.Entry; - /* Copyright (c) 2002 JSON.org @@ -24,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, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ + */ /** * Convert a web browser cookie list string to a JSONObject and back. @@ -69,17 +67,17 @@ public class CookieList { */ public static String toString(JSONObject jo) throws JSONException { boolean b = false; - StringBuilder sb = new StringBuilder(); - for (final Entry entry : jo.entrySet()) { - final String key = entry.getKey(); - final Object value = entry.getValue(); + final StringBuilder sb = new StringBuilder(); + // Don't use the new entrySet API to maintain Android support + for (final String key : jo.keySet()) { + final Object value = jo.opt(key); if (!JSONObject.NULL.equals(value)) { if (b) { sb.append(';'); } sb.append(Cookie.escape(key)); sb.append("="); - sb.append(Cookie.escape(value.toString())); + sb.append(Cookie.escape(value.toString())); b = true; } } diff --git a/HTTP.java b/HTTP.java index 22635ff..70b88ee 100644 --- a/HTTP.java +++ b/HTTP.java @@ -25,7 +25,6 @@ SOFTWARE. */ import java.util.Locale; -import java.util.Map.Entry; /** * Convert an HTTP header to a JSONObject and back. @@ -145,11 +144,12 @@ public class HTTP { throw new JSONException("Not enough material for an HTTP header."); } sb.append(CRLF); - for (final Entry entry : jo.entrySet()) { - final String key = entry.getKey(); + // Don't use the new entrySet API to maintain Android support + for (final String key : jo.keySet()) { + String value = jo.optString(key); if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) && !"Reason-Phrase".equals(key) && !"Method".equals(key) && - !"Request-URI".equals(key) && !JSONObject.NULL.equals(entry.getValue())) { + !"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) { sb.append(key); sb.append(": "); sb.append(jo.optString(key)); diff --git a/JSONML.java b/JSONML.java index be16693..acec7b8 100644 --- a/JSONML.java +++ b/JSONML.java @@ -1,7 +1,5 @@ package org.json; -import java.util.Map.Entry; - /* Copyright (c) 2008 JSON.org @@ -416,10 +414,10 @@ public class JSONML { // Emit the attributes - for (final Entry entry : jo.entrySet()) { - final String key = entry.getKey(); + // Don't use the new entrySet API to maintain Android support + for (final String key : jo.keySet()) { + final Object value = jo.opt(key); XML.noSpace(key); - final Object value = entry.getValue(); if (value != null) { sb.append(' '); sb.append(XML.escape(key)); @@ -495,11 +493,11 @@ public class JSONML { //Emit the attributes - for (final Entry entry : jo.entrySet()) { - final String key = entry.getKey(); + // Don't use the new entrySet API to maintain Android support + for (final String key : jo.keySet()) { if (!"tagName".equals(key) && !"childNodes".equals(key)) { XML.noSpace(key); - value = entry.getValue(); + value = jo.opt(key); if (value != null) { sb.append(' '); sb.append(XML.escape(key)); diff --git a/JSONObject.java b/JSONObject.java index 1b7b0a1..54efa6e 100644 --- a/JSONObject.java +++ b/JSONObject.java @@ -1950,6 +1950,8 @@ public class JSONObject { * A String. * @return A simple JSON value. */ + // Changes to this method must be copied to the corresponding method in + // the XML class to keep full support for Android public static Object stringToValue(String string) { if (string.equals("")) { return string; @@ -2120,55 +2122,11 @@ public class JSONObject { * If the value is or contains an invalid number. */ public static String valueToString(Object value) throws JSONException { - if (value == null || value.equals(null)) { - return "null"; - } - if (value instanceof JSONString) { - Object object; - try { - object = ((JSONString) value).toJSONString(); - } catch (Exception e) { - throw new JSONException(e); - } - if (object instanceof String) { - return (String) object; - } - throw new JSONException("Bad value from toJSONString: " + object); - } - if (value instanceof Number) { - // 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. - @SuppressWarnings("unused") - BigDecimal unused = 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) { - return value.toString(); - } - if (value instanceof Map) { - Map map = (Map) value; - return new JSONObject(map).toString(); - } - if (value instanceof Collection) { - Collection coll = (Collection) value; - return new JSONArray(coll).toString(); - } - if (value.getClass().isArray()) { - return new JSONArray(value).toString(); - } - if(value instanceof Enum){ - return quote(((Enum)value).name()); - } - return quote(value.toString()); + // moves the implementation to JSONWriter as: + // 1. It makes more sense to be part of the writer class + // 2. For Android support this method is not available. By implementing it in the Writer + // Android users can use the writer with the built in Android JSONObject implementation. + return JSONWriter.valueToString(value); } /** diff --git a/JSONPointer.java b/JSONPointer.java index 8142f9a..0040e17 100644 --- a/JSONPointer.java +++ b/JSONPointer.java @@ -5,7 +5,9 @@ import static java.lang.String.format; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /* Copyright (c) 2002 JSON.org @@ -181,7 +183,7 @@ public class JSONPointer { * @return the result of the evaluation * @throws JSONPointerException if an error occurs during evaluation */ - public Object queryFrom(Object document) { + public Object queryFrom(Object document) throws JSONPointerException { if (this.refTokens.isEmpty()) { return document; } @@ -205,10 +207,9 @@ public class JSONPointer { * @param current the JSONArray to be evaluated * @param indexToken the array index in string form * @return the matched object. If no matching item is found a - * JSONPointerException is thrown + * @throws JSONPointerException is thrown if the index is out of bounds */ - @SuppressWarnings("boxing") - private Object readByIndexToken(Object current, String indexToken) { + private Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { try { int index = Integer.parseInt(indexToken); JSONArray currentArr = (JSONArray) current; @@ -216,7 +217,11 @@ public class JSONPointer { throw new JSONPointerException(format("index %d is out of bounds - the array has %d elements", index, currentArr.length())); } - return currentArr.get(index); + try { + return currentArr.get(index); + } catch (JSONException e) { + throw new JSONPointerException("Error reading value at index position " + index, e); + } } catch (NumberFormatException e) { throw new JSONPointerException(format("%s is not an array index", indexToken), e); } diff --git a/JSONWriter.java b/JSONWriter.java index 549f93e..ac5a805 100644 --- a/JSONWriter.java +++ b/JSONWriter.java @@ -1,6 +1,9 @@ package org.json; import java.io.IOException; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Map; /* Copyright (c) 2006 JSON.org @@ -117,6 +120,9 @@ public class JSONWriter { } this.writer.append(string); } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. throw new JSONException(e); } if (this.mode == 'o') { @@ -164,6 +170,9 @@ public class JSONWriter { try { this.writer.append(c); } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. throw new JSONException(e); } this.comma = true; @@ -204,7 +213,12 @@ public class JSONWriter { } if (this.mode == 'k') { try { - this.stack[this.top - 1].putOnce(string, Boolean.TRUE); + JSONObject topObject = this.stack[this.top - 1]; + // don't use the built in putOnce method to maintain Android support + if(topObject.has(string)) { + throw new JSONException("Duplicate key \"" + string + "\""); + } + topObject.put(string, true); if (this.comma) { this.writer.append(','); } @@ -214,6 +228,9 @@ public class JSONWriter { this.mode = 'o'; return this; } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. throw new JSONException(e); } } @@ -280,6 +297,81 @@ public class JSONWriter { this.top += 1; } + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce the + * JSON text. The method is required to produce a strictly conforming text. + * If the object does not contain a toJSONString method (which is the most + * common case), then a text will be produced by other means. If the value + * is an array or Collection, then a JSONArray will be made from it and its + * toJSONString method will be called. If the value is a MAP, then a + * JSONObject will be made from it and its toJSONString method will be + * called. Otherwise, the value's toString method will be called, and the + * result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @param value + * The value to be serialized. + * @return a printable, displayable, transmittable representation of the + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException + * If the value is or contains an invalid number. + */ + public static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + Object object; + try { + object = ((JSONString) value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (object instanceof String) { + return (String) object; + } + throw new JSONException("Bad value from toJSONString: " + object); + } + if (value instanceof Number) { + // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex + final String numberAsString = JSONObject.numberToString((Number) value); + try { + // Use the BigDecimal constructor for it's parser to validate the format. + @SuppressWarnings("unused") + BigDecimal unused = new BigDecimal(numberAsString); + // Close enough to a JSON number that we will return it unquoted + return numberAsString; + } catch (NumberFormatException ex){ + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + return JSONObject.quote(numberAsString); + } + } + if (value instanceof Boolean || value instanceof JSONObject + || value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + Map map = (Map) value; + return new JSONObject(map).toString(); + } + if (value instanceof Collection) { + Collection coll = (Collection) value; + return new JSONArray(coll).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + if(value instanceof Enum){ + return JSONObject.quote(((Enum)value).name()); + } + return JSONObject.quote(value.toString()); + } /** * Append either the value true or the value @@ -321,6 +413,6 @@ public class JSONWriter { * @throws JSONException If the value is out of sequence. */ public JSONWriter value(Object object) throws JSONException { - return this.append(JSONObject.valueToString(object)); + return this.append(valueToString(object)); } } diff --git a/Property.java b/Property.java index 51b97ed..ff33a04 100644 --- a/Property.java +++ b/Property.java @@ -25,7 +25,6 @@ SOFTWARE. */ import java.util.Enumeration; -import java.util.Map.Entry; import java.util.Properties; /** @@ -41,7 +40,9 @@ public class Property { * @throws JSONException */ public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException { - JSONObject jo = new JSONObject(properties == null ? 0 : properties.size()); + // can't use the new constructor for Android support + // JSONObject jo = new JSONObject(properties == null ? 0 : properties.size()); + JSONObject jo = new JSONObject(); if (properties != null && !properties.isEmpty()) { Enumeration enumProperties = properties.propertyNames(); while(enumProperties.hasMoreElements()) { @@ -61,10 +62,11 @@ public class Property { public static Properties toProperties(JSONObject jo) throws JSONException { Properties properties = new Properties(); if (jo != null) { - for (final Entry entry : jo.entrySet()) { - Object value = entry.getValue(); + // Don't use the new entrySet API to maintain Android support + for (final String key : jo.keySet()) { + Object value = jo.opt(key); if (!JSONObject.NULL.equals(value)) { - properties.put(entry.getKey(), value.toString()); + properties.put(key, value.toString()); } } } diff --git a/XML.java b/XML.java index faa5b65..08666f7 100644 --- a/XML.java +++ b/XML.java @@ -25,7 +25,6 @@ SOFTWARE. */ import java.util.Iterator; -import java.util.Map.Entry; /** * This provides static methods to convert an XML text into a JSONObject, and to @@ -399,14 +398,56 @@ public class XML { } /** - * This method is the same as {@link JSONObject#stringToValue(String)} - * except that this also tries to unescape String values. + * This method is the same as {@link JSONObject#stringToValue(String)}. * * @param string String to convert * @return JSON value of this string or the string */ + // To maintain compatibility with the Android API, this method is a direct copy of + // the one in JSONObject. Changes made here should be reflected there. public static Object stringToValue(String string) { - return JSONObject.stringToValue(string); + if (string.equals("")) { + return string; + } + if (string.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (string.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (string.equalsIgnoreCase("null")) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. If a number cannot be + * produced, then the value will just be a string. + */ + + char initial = string.charAt(0); + if ((initial >= '0' && initial <= '9') || initial == '-') { + try { + // if we want full Big Number support this block can be replaced with: + // return stringToNumber(string); + if (string.indexOf('.') > -1 || string.indexOf('e') > -1 + || string.indexOf('E') > -1 || "-0".equals(string)) { + Double d = Double.valueOf(string); + if (!d.isInfinite() && !d.isNaN()) { + return d; + } + } else { + Long myLong = Long.valueOf(string); + if (string.equals(myLong.toString())) { + if (myLong.longValue() == myLong.intValue()) { + return Integer.valueOf(myLong.intValue()); + } + return myLong; + } + } + } catch (Exception ignore) { + } + } + return string; } /** @@ -454,8 +495,11 @@ public class XML { public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(string); - while (x.more() && x.skipPast("<")) { - parse(x, jo, null, keepStrings); + while (x.more()) { + x.skipPast("<"); + if(x.more()) { + parse(x, jo, null, keepStrings); + } } return jo; } @@ -498,10 +542,10 @@ public class XML { } // Loop thru the keys. + // don't use the new entrySet accessor to maintain Android Support jo = (JSONObject) object; - for (final Entry entry : jo.entrySet()) { - final String key = entry.getKey(); - Object value = entry.getValue(); + for (final String key : jo.keySet()) { + Object value = jo.opt(key); if (value == null) { value = ""; } else if (value.getClass().isArray()) { @@ -512,13 +556,14 @@ public class XML { if ("content".equals(key)) { if (value instanceof JSONArray) { ja = (JSONArray) value; - int i = 0; - for (Object val : ja) { + int jaLength = ja.length(); + // don't use the new iterator API to maintain support for Android + for (int i = 0; i < jaLength; i++) { if (i > 0) { sb.append('\n'); } + Object val = ja.opt(i); sb.append(escape(val.toString())); - i++; } } else { sb.append(escape(value.toString())); @@ -528,7 +573,10 @@ public class XML { } else if (value instanceof JSONArray) { ja = (JSONArray) value; - for (Object val : ja) { + int jaLength = ja.length(); + // don't use the new iterator API to maintain support for Android + for (int i = 0; i < jaLength; i++) { + Object val = ja.opt(i); if (val instanceof JSONArray) { sb.append('<'); sb.append(key); @@ -569,7 +617,10 @@ public class XML { } else { ja = (JSONArray) object; } - for (Object val : ja) { + int jaLength = ja.length(); + // don't use the new iterator API to maintain support for Android + for (int i = 0; i < jaLength; i++) { + Object val = ja.opt(i); // XML does not have good support for arrays. If an array // appears in a place where XML is lacking, synthesize an // element. diff --git a/XMLTokener.java b/XMLTokener.java index fb54da3..1da8b84 100644 --- a/XMLTokener.java +++ b/XMLTokener.java @@ -326,9 +326,11 @@ public class XMLTokener extends JSONTokener { * Skip characters until past the requested string. * If it is not found, we are left at the end of the source with a result of false. * @param to A string to skip past. - * @throws JSONException */ - public boolean skipPast(String to) throws JSONException { + // The Android implementation of JSONTokener has a public method of public void skipPast(String to) + // even though ours does not have that method, to have API compatibility, our method in the subclass + // should match. + public void skipPast(String to) { boolean b; char c; int i; @@ -345,7 +347,7 @@ public class XMLTokener extends JSONTokener { for (i = 0; i < length; i += 1) { c = next(); if (c == 0) { - return false; + return; } circle[i] = c; } @@ -372,14 +374,14 @@ public class XMLTokener extends JSONTokener { /* If we exit the loop with b intact, then victory is ours. */ if (b) { - return true; + return; } /* Get the next character. If there isn't one, then defeat is ours. */ c = next(); if (c == 0) { - return false; + return; } /* * Shove the character in the circle buffer and advance the