JSONObject.toString(int) 怎么会抛出 JSONException?

How can JSONObject.toString(int) throw JSONException?

我在 JSONObject.toString(int) 文档中注意到了这一点

Throws: JSONException - If the object contains an invalid number.

如果事先进行了 NaN/Infinity 检查(在解析时和 .put("blah", Double.NAN) 之类的语句中,对象怎么可能包含无效数字? 哪些语句会抛出该异常?

我还在文档中读到 .toString() 没有 args 不会抛出任何东西,所以我认为异常可能与作为参数传递的整数有关;但即使是零、负数或整数。MAX/MIN_VALUE 它仍然有效。

那么什么时候应该期望 .toString(int) 抛出异常,应该如何处理呢?

toString() 方法不会抛出异常,因为它必须覆盖 java.lang.Objectpublic void toString() 方法的签名。在 org.json.JSONObject 中,通用 toString() 方法实际上失败了,因为源代码是:

/**
 * 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>&nbsp;<small>(left
 *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 *         brace)</small>.
 */
public String toString() {
    try {
        return this.toString(0);
    } catch (Exception e) {
        return null;
    }
}

这个方法依赖于toString(int)方法,如果它抛出异常,它会捕获它并且returns null。

根据toString(int)的描述,当org.json.JSONObject的其中一个元素中有一个无效数字时抛出异常;但是查看代码可能会因为其他原因抛出此异常。

当您使用 toString(int) 堆栈跟踪最终调用 write() 方法来解析对象本身时,对于从 json 对象到字符串的某些转换可能会抛出异常:

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;
    }

但是,如果正如您在问题中所说(和@Carlos Rodriguez 评论),所有检查都是在创建对象时执行的,可能 toString(int) 方法从未抛出异常。

希望对您有所帮助,

查看 JSONObject.toString(int)JSONObject.toString() 的实现会有所帮助:

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

write 方法写入 Writer 并将任何 IOException 作为 JSONException 重新抛出。如果您的分析是正确的,那么这样的 IOException 将永远不会发生,因为 StringWriters 也不会抛出 IOException。因此,让此方法抛出 JSONException 似乎只是一个实现决定,文档具有误导性。

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

toString() 实现证实了这个分析:这里 API 设计者决定捕获任何异常,这只有在 从未 抛出异常时才有意义.

最好将 try-catch 移动到 toString(int) 中,以便将这两个方法从其签名中的 throw 子句中释放出来。

我刚刚 运行 遇到这个问题。

这是一个示例,它会导致 JSONObject.toString(int) 方法抛出 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);
}

在我看来应该由 JSONObject.put() 方法抛出异常,但显然这里没有进行检查...