How can JSONObject.toString(int) throw JSONException? How can JSONObject.toString(int) throw JSONException? json json

How can JSONObject.toString(int) throw JSONException?


toString() method not throws an exception because it has to override a signature of public void toString() method of java.lang.Object. In org.json.JSONObject generic toString() method actually fails silently since the source code is:

/** * Make a JSON text of this JSONObject. For compactness, no whitespace is * added. If this would not result in a syntactically correct JSON text, * then null will be returned instead. * <p> * Warning: This method assumes that the data structure is acyclical. * * @return a printable, displayable, portable, transmittable representation *         of the object, beginning with <code>{</code> <small>(left *         brace)</small> and ending with <code>}</code> <small>(right *         brace)</small>. */public String toString() {    try {        return this.toString(0);    } catch (Exception e) {        return null;    }}

This method relies on toString(int) method, and in case that it throws the exception, it catch it and returns null.

According to the toString(int) description, the exception is thrown when org.json.JSONObject has an invalid number inside one of its elements; but looking at code could be possible that this exception is thrown for other reasons.

When you use toString(int) the stack trace finally calls write() method to parse the object itself, the exception could be thrown for some of transformations from json objects to string:

static final Writer writeValue(Writer writer, Object value,            int indentFactor, int indent) throws JSONException, IOException {        if (value == null || value.equals(null)) {            writer.write("null");        } else if (value instanceof JSONObject) {            ((JSONObject) value).write(writer, indentFactor, indent);        } else if (value instanceof JSONArray) {            ((JSONArray) value).write(writer, indentFactor, indent);        } else if (value instanceof Map) {            new JSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);        } else if (value instanceof Collection) {            new JSONArray((Collection<Object>) value).write(writer, indentFactor,                    indent);        } else if (value.getClass().isArray()) {            new JSONArray(value).write(writer, indentFactor, indent);        } else if (value instanceof Number) {            writer.write(numberToString((Number) value));        } else if (value instanceof Boolean) {            writer.write(value.toString());        } else if (value instanceof JSONString) {            Object o;            try {                o = ((JSONString) value).toJSONString();            } catch (Exception e) {                throw new JSONException(e);            }            writer.write(o != null ? o.toString() : quote(value.toString()));        } else {            quote(value.toString(), writer);        }        return writer;    }

However if as you said in your question (and @Carlos Rodriguez comments) all the checks are performed when the object is created, probably toString(int) method never thrown an exception.

Hope it helps,


It helps to look at the implementation of JSONObject.toString(int) and JSONObject.toString():

public String toString(int indentFactor) throws JSONException {    StringWriter w = new StringWriter();    synchronized (w.getBuffer()) {        return this.write(w, indentFactor, 0).toString();    }} 

The write method writes to a Writer and rethrows any IOException as JSONException. If your analysis is right, then such an IOException would never occur, since StringWriters also don't throw IOExceptions. So it seems merely an implementation decision to let this method throw an JSONException and the documentation is misleading.

public String toString() {    try {        return this.toString(0);    } catch (Exception e) {        return null;    }} 

The toString() implementation confirms this analysis: Here the API designers decided to catch any Exception which would only make sense if an Exception is never thrown.

It would have probably been better to move the try-catch into the toString(int) to free both method from the throw clause in their signatures.


I've just run into this problem.

Here is an example that will cause the JSONObject.toString(int) method to throw a JSONException:

public static void main(String[] args) {    Map<String, Double> map = new HashMap<>();    map.put("num", Double.NaN);    JSONObject obj = new JSONObject();    obj.put("map", map);    obj.toString(0);}

In my opinion the exception should be thrown by the JSONObject.put() method, but apparently no check is done here...