diff --git a/JSONArray.java b/JSONArray.java index 8ac58441b..c876f42a9 100644 --- a/JSONArray.java +++ b/JSONArray.java @@ -25,8 +25,6 @@ of this software and associated documentation files (the "Software"), to deal */ import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; @@ -78,7 +76,7 @@ of this software and associated documentation files (the "Software"), to deal * * * @author JSON.org - * @version 2016-07-19 + * @version 2016-08-04 */ public class JSONArray implements Iterable { @@ -447,7 +445,7 @@ public String join(String separator) throws JSONException { if (i > 0) { sb.append(separator); } - sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + JSONObject.writeValue(sb, this.myArrayList.get(i)); } return sb.toString(); } @@ -1017,7 +1015,7 @@ public JSONArray put(int index, Object value) throws JSONException { } /** - * Creates a JSONPointer using an initialization string and tries to + * Creates a JSONPointer using an initialization string and tries to * match it to an item within this JSONArray. For example, given a * JSONArray initialized with this document: *
@@ -1159,10 +1157,8 @@ public String toString() {
      * @throws JSONException
      */
     public String toString(int indentFactor) throws JSONException {
-        StringWriter sw = new StringWriter();
-        synchronized (sw.getBuffer()) {
-            return this.write(sw, indentFactor, 0).toString();
-        }
+        StringBuilder sw = new StringBuilder();
+        return this.write(sw, indentFactor, 0).toString();
     }
 
     /**
@@ -1174,7 +1170,7 @@ public String toString(int indentFactor) throws JSONException {
      * @return The writer.
      * @throws JSONException
      */
-    public Writer write(Writer writer) throws JSONException {
+    public  T write(T writer) throws JSONException {
         return this.write(writer, 0, 0);
     }
 
@@ -1193,12 +1189,12 @@ public Writer write(Writer writer) throws JSONException {
      * @return The writer.
      * @throws JSONException
      */
-    public Writer write(Writer writer, int indentFactor, int indent)
+    public  T write(T writer, int indentFactor, int indent)
             throws JSONException {
         try {
             boolean commanate = false;
             int length = this.length();
-            writer.write('[');
+            writer.append('[');
 
             if (length == 1) {
                 JSONObject.writeValue(writer, this.myArrayList.get(0),
@@ -1208,10 +1204,10 @@ public Writer write(Writer writer, int indentFactor, int indent)
 
                 for (int i = 0; i < length; i += 1) {
                     if (commanate) {
-                        writer.write(',');
+                        writer.append(',');
                     }
                     if (indentFactor > 0) {
-                        writer.write('\n');
+                        writer.append('\n');
                     }
                     JSONObject.indent(writer, newindent);
                     JSONObject.writeValue(writer, this.myArrayList.get(i),
@@ -1219,11 +1215,11 @@ public Writer write(Writer writer, int indentFactor, int indent)
                     commanate = true;
                 }
                 if (indentFactor > 0) {
-                    writer.write('\n');
+                    writer.append('\n');
                 }
                 JSONObject.indent(writer, indent);
             }
-            writer.write(']');
+            writer.append(']');
             return writer;
         } catch (IOException e) {
             throw new JSONException(e);
diff --git a/JSONObject.java b/JSONObject.java
index 8fb0a2569..20cdaa270 100644
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -25,8 +25,6 @@ of this software and associated documentation files (the "Software"), to deal
  */
 
 import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -93,7 +91,7 @@ of this software and associated documentation files (the "Software"), to deal
  * 
  *
  * @author JSON.org
- * @version 2016-07-19
+ * @version 2016-08-04
  */
 public class JSONObject {
     /**
@@ -1442,21 +1440,19 @@ public Object optQuery(String jsonPointer) {
      *            A String
      * @return A String correctly formatted for insertion in a JSON text.
      */
-    public static String quote(String string) {
-        StringWriter sw = new StringWriter();
-        synchronized (sw.getBuffer()) {
-            try {
-                return quote(string, sw).toString();
-            } catch (IOException ignored) {
-                // will never happen - we are writing to a string writer
-                return "";
-            }
+    public static String quote(CharSequence string) {
+        StringBuilder sb = new StringBuilder();
+        try {
+            return quote(string, sb).toString();
+        } catch (IOException ignored) {
+            // will never happen - we are writing to a string builder
+            return "";
         }
     }
 
-    public static Writer quote(String string, Writer w) throws IOException {
+    public static  T quote(CharSequence string, T w) throws IOException {
         if (string == null || string.length() == 0) {
-            w.write("\"\"");
+            w.append("\"\"");
             return w;
         }
 
@@ -1465,51 +1461,84 @@ public static Writer quote(String string, Writer w) throws IOException {
         String hhhh;
         int i;
         int len = string.length();
+        int prev = 0;
 
-        w.write('"');
+        w.append('"');
         for (i = 0; i < len; i += 1) {
             b = c;
             c = string.charAt(i);
             switch (c) {
             case '\\':
             case '"':
-                w.write('\\');
-                w.write(c);
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append('\\');
+                prev = i;
                 break;
             case '/':
                 if (b == '<') {
-                    w.write('\\');
+                    if(prev < i) {
+                        w.append(string, prev, i);
+                    }
+                    w.append('\\');
+                    prev = i;
                 }
-                w.write(c);
                 break;
             case '\b':
-                w.write("\\b");
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append("\\b");
+                prev = i + 1;
                 break;
             case '\t':
-                w.write("\\t");
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append("\\t");
+                prev = i + 1;
                 break;
             case '\n':
-                w.write("\\n");
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append("\\n");
+                prev = i + 1;
                 break;
             case '\f':
-                w.write("\\f");
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append("\\f");
+                prev = i + 1;
                 break;
             case '\r':
-                w.write("\\r");
+                if(prev < i) {
+                    w.append(string, prev, i);
+                }
+                w.append("\\r");
+                prev = i + 1;
                 break;
             default:
                 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
                         || (c >= '\u2000' && c < '\u2100')) {
-                    w.write("\\u");
+                    if(prev < i) {
+                        w.append(string, prev, i);
+                    }
                     hhhh = Integer.toHexString(c);
-                    w.write("0000", 0, 4 - hhhh.length());
-                    w.write(hhhh);
-                } else {
-                    w.write(c);
+                    w.append("\\u0000", 0, 6 - hhhh.length());
+                    w.append(hhhh);
+                    prev = i + 1;
                 }
             }
         }
-        w.write('"');
+        if(prev == 0) {
+            w.append(string);
+        } else if(prev < len) {
+            w.append(string, prev, len);
+        }
+        w.append('"');
         return w;
     }
 
@@ -1699,10 +1728,8 @@ public String toString() {
      *             If the object contains an invalid number.
      */
     public String toString(int indentFactor) throws JSONException {
-        StringWriter w = new StringWriter();
-        synchronized (w.getBuffer()) {
-            return this.write(w, indentFactor, 0).toString();
-        }
+        StringBuilder w = new StringBuilder();
+        return this.write(w, indentFactor, 0).toString();
     }
 
     /**
@@ -1730,40 +1757,7 @@ public String toString(int indentFactor) 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) {
-            return numberToString((Number) value);
-        }
-        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();
-        }
-        return quote(value.toString());
+        return writeValue(new StringBuilder(), value).toString();
     }
 
     /**
@@ -1828,14 +1822,23 @@ public static Object wrap(Object object) {
      * @return The writer.
      * @throws JSONException
      */
-    public Writer write(Writer writer) throws JSONException {
+    public  T write(T writer) throws JSONException {
         return this.write(writer, 0, 0);
     }
 
-    static final Writer writeValue(Writer writer, Object value,
+    static  T writeValue(T writer, Object value)
+            throws JSONException {
+        try {
+            return writeValue(writer, value, 0 ,0);
+        } catch (IOException e) {
+            throw new JSONException(e);
+        }
+    }
+
+    static final  T writeValue(T writer, Object value,
             int indentFactor, int indent) throws JSONException, IOException {
         if (value == null || value.equals(null)) {
-            writer.write("null");
+            writer.append("null");
         } else if (value instanceof JSONObject) {
             ((JSONObject) value).write(writer, indentFactor, indent);
         } else if (value instanceof JSONArray) {
@@ -1849,26 +1852,30 @@ static final Writer writeValue(Writer writer, Object value,
         } else if (value.getClass().isArray()) {
             new JSONArray(value).write(writer, indentFactor, indent);
         } else if (value instanceof Number) {
-            writer.write(numberToString((Number) value));
+            writer.append(numberToString((Number) value));
         } else if (value instanceof Boolean) {
-            writer.write(value.toString());
+            writer.append(value.toString());
         } else if (value instanceof JSONString) {
-            Object o;
+            String o;
             try {
                 o = ((JSONString) value).toJSONString();
             } catch (Exception e) {
                 throw new JSONException(e);
             }
-            writer.write(o != null ? o.toString() : quote(value.toString()));
+            if (o != null) {
+                writer.append(o);
+            } else {
+                quote(value.toString(), writer);
+            }
         } else {
             quote(value.toString(), writer);
         }
         return writer;
     }
 
-    static final void indent(Writer writer, int indent) throws IOException {
+    static final  void indent(T writer, int indent) throws IOException {
         for (int i = 0; i < indent; i += 1) {
-            writer.write(' ');
+            writer.append(' ');
         }
     }
 
@@ -1887,47 +1894,47 @@ static final void indent(Writer writer, int indent) throws IOException {
      * @return The writer.
      * @throws JSONException
      */
-    public Writer write(Writer writer, int indentFactor, int indent)
+    public  T write(T writer, int indentFactor, int indent)
             throws JSONException {
         try {
             boolean commanate = false;
             final int length = this.length();
             Iterator keys = this.keys();
-            writer.write('{');
+            writer.append('{');
 
             if (length == 1) {
-                Object key = keys.next();
-                writer.write(quote(key.toString()));
-                writer.write(':');
+                String key = keys.next();
+                quote(key, writer);
+                writer.append(':');
                 if (indentFactor > 0) {
-                    writer.write(' ');
+                    writer.append(' ');
                 }
                 writeValue(writer, this.map.get(key), indentFactor, indent);
             } else if (length != 0) {
                 final int newindent = indent + indentFactor;
                 while (keys.hasNext()) {
-                    Object key = keys.next();
+                    String key = keys.next();
                     if (commanate) {
-                        writer.write(',');
+                        writer.append(',');
                     }
                     if (indentFactor > 0) {
-                        writer.write('\n');
+                        writer.append('\n');
                     }
                     indent(writer, newindent);
-                    writer.write(quote(key.toString()));
-                    writer.write(':');
+                    quote(key, writer);
+                    writer.append(':');
                     if (indentFactor > 0) {
-                        writer.write(' ');
+                        writer.append(' ');
                     }
                     writeValue(writer, this.map.get(key), indentFactor, newindent);
                     commanate = true;
                 }
                 if (indentFactor > 0) {
-                    writer.write('\n');
+                    writer.append('\n');
                 }
                 indent(writer, indent);
             }
-            writer.write('}');
+            writer.append('}');
             return writer;
         } catch (IOException exception) {
             throw new JSONException(exception);
diff --git a/JSONString.java b/JSONString.java
index 1f2d77dd1..26f8601aa 100644
--- a/JSONString.java
+++ b/JSONString.java
@@ -1,4 +1,29 @@
 package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
 /**
  * The JSONString interface allows a toJSONString()
  * method so that a class can change the behavior of
@@ -6,13 +31,25 @@
  * and JSONWriter.value(Object). The
  * toJSONString method will be used instead of the default behavior
  * of using the Object's toString() method and quoting the result.
+ * 

+ * If toJSONString() returns null, the class's + * toString() value will be quoted and used.

+ * + * @author json.org + * @version 2016-07-28 */ public interface JSONString { /** * The toJSONString method allows a class to produce its own JSON - * serialization. + * serialization. If null is returned, the class's + * toString method will be called, and the result will be + * quoted and used. + *

+ * If a runtime exception is thrown, this will be caught and propagated + * as a JSONException.

* - * @return A strictly syntactically correct JSON text. + * @return A strictly syntactically correct JSON text, or null + * to indicate the toString value should be used instead */ public String toJSONString(); } diff --git a/JSONStringer.java b/JSONStringer.java index 5fbc96a9a..056913373 100644 --- a/JSONStringer.java +++ b/JSONStringer.java @@ -24,8 +24,6 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ -import java.io.StringWriter; - /** * JSONStringer provides a quick and convenient way of producing JSON text. * The texts produced strictly conform to JSON syntax rules. No whitespace is @@ -54,14 +52,14 @@ of this software and associated documentation files (the "Software"), to deal *

* This can sometimes be easier than using a JSONObject to build a string. * @author JSON.org - * @version 2015-12-09 + * @version 2016-07-28 */ public class JSONStringer extends JSONWriter { /** * Make a fresh JSONStringer. It can be used to build one JSON text. */ public JSONStringer() { - super(new StringWriter()); + super(new StringBuilder()); } /** diff --git a/JSONWriter.java b/JSONWriter.java index 09d113030..ea5b06fd4 100644 --- a/JSONWriter.java +++ b/JSONWriter.java @@ -1,7 +1,6 @@ package org.json; import java.io.IOException; -import java.io.Writer; /* Copyright (c) 2006 JSON.org @@ -50,11 +49,11 @@ of this software and associated documentation files (the "Software"), to deal *

* The first method called must be array or object. * 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. *

* This can sometimes be easier than using a JSONObject to build a string. * @author JSON.org - * @version 2015-12-09 + * @version 2016-08-04 */ 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]; @@ -102,7 +101,7 @@ public JSONWriter(Writer w) { } /** - * Append a value. + * Append a JSON-encoded value. * @param string A string value. * @return this * @throws JSONException If the value is out of sequence. @@ -114,9 +113,9 @@ private JSONWriter append(String string) throws JSONException { 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); } @@ -129,6 +128,35 @@ private JSONWriter append(String string) throws JSONException { throw new JSONException("Value out of sequence."); } + /** + * Append a value, converting it into a JSON string. + * + * @param val A value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter appendValue(Object val) throws JSONException { + try { + switch (this.mode) { + case 'a': + if (this.comma) { + this.writer.append(','); + } + break; + case 'o': + this.mode = 'k'; + break; + default: + throw new JSONException("Value out of sequence."); + } + JSONObject.writeValue(this.writer, val); + this.comma = true; + return this; + } catch (IOException e) { + throw new JSONException(e); + } + } + /** * Begin appending a new array. All values until the balancing * endArray will be appended to this array. The @@ -163,7 +191,7 @@ private JSONWriter end(char mode, char c) throws JSONException { } this.pop(mode); try { - this.writer.write(c); + this.writer.append(c); } catch (IOException e) { throw new JSONException(e); } @@ -207,10 +235,10 @@ public JSONWriter key(String string) throws JSONException { 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(':'); + JSONObject.quote(string, this.writer); + this.writer.append(':'); this.comma = false; this.mode = 'o'; return this; @@ -300,7 +328,7 @@ public JSONWriter value(boolean b) throws JSONException { * @throws JSONException If the number is not finite. */ public JSONWriter value(double d) throws JSONException { - return this.value(new Double(d)); + return this.append(JSONObject.numberToString(Double.valueOf(d))); } /** @@ -322,6 +350,6 @@ public JSONWriter value(long l) throws JSONException { * @throws JSONException If the value is out of sequence. */ public JSONWriter value(Object object) throws JSONException { - return this.append(JSONObject.valueToString(object)); + return this.appendValue(object); } }