之前介绍了如何Map 进行遍历,里面提到了一个场景,就是将缓存到 Map 的数据存储到 JSONObject 中,我们知道,JSON 是一种与 Map 十分相似的数据交换格式,它同样采用了键值对的存储方式,那么现在就假设一个相反的场景,当我们需要将 JSON 中的数据缓存到本地时,也许遍历也不失为一种优雅的做法。

Map 的遍历相似,这个场景归根结底在于对 JSONkey 的遍历,因为只要拿到 key,也就可以轻易拿到 value

JSONObject 的遍历也有多种实现方式:

Iterator.hasNext()

public static void jsonTraversal(JSONObject json) {
    Iterator<String> it = json.keys();
    while (it.hasNext()) {
        String key = it.next();
        Object value = json.opt(key);
        // Do something...
    }
}

JSONObjectkeys() 方法会返回其所有 keySet 集合的 Iterator 对象,因此我们可以直接对其迭代。

Iterator.forEachRemaining()

public static void jsonTraversal(JSONObject json) {
    json.keys().forEachRemaining(key -> {
    Object value = json.opt(key);
    // Do something...
}

Java 8 之后 Iterator 也增加了 forEachRemaining() 方法,Lambda 的写法更加简洁。

JSONObject.names()

public static void jsonTraversal(JSONObject json) {
    JSONArray array = json.names();
    if (array != null) {
        for (int i = 0; i < array.length(); i++) {
            String key = array.optString(i);
            Object value = json.opt(key);
            // Do something...
        }
    }
}

JSONObject 中有个 names() 方法很有意思,它把 key 集合通过 JSONArray 也就是数组的形式返回,只需遍历这个数组将 key 值取出即可。

不过当 JSONObject 中不存在数据时,该方法会返回 null,所以记得判空。

JSONObject.keySet()

public static void jsonTraversal(JSONObject json) {
    for (String key : json.keySet()) {
        Object value = json.opt(key);
        // Do something...
    }
}

我们知道 Map 中有 keySet() 方法,JSONObject 也有,返回的是一个包含所有 keySet 集合,对其进行遍历即可。

同样,Java 8 上也可以用上 forEach()

public static void jsonTraversal(JSONObject json) {
    json.keySet().forEach(key -> {
        Object value = json.opt(key);
        // Do something...
    });
}

但是当在 Android 中使用 keySet() 时你会发现找不到该方法,查看源码可以看到其被限制了外部访问:

public class JSONObject {
    /**
     * Returns the set of {@code String} names in this object. The returned set
     * is a view of the keys in this object. {@link Set#remove(Object)} will remove
     * the corresponding mapping from this object and set iterator behaviour
     * is undefined if this object is modified after it is returned.
     *
     * See {@link #keys()}.
     *
     * @hide.
     */
    @UnsupportedAppUsage
    @libcore.api.CorePlatformApi
    public Set<String> keySet() {
        return nameValuePairs.keySet();
    }
}

而在普通的 Java 项目中引入最新的 org.json 库,是支持外部调用的,估计是 Android 官方出于某些原因考虑而将其限制了,又或者是 Android 源码中引入的是低版本 org.json 库的原因,因为我在最新的版本中看到的 entrySet() 方法,Android 源码中也没有:

public class JSONObject {
    /**
     * Get a set of entries of the JSONObject. These are raw values and may not
     * match what is returned by the JSONObject get* and opt* functions. Modifying
     * the returned EntrySet or the Entry objects contained therein will modify the
     * backing JSONObject. This does not return a clone or a read-only view.
     *
     * Use with caution.
     *
     * @see Map#entrySet()
     *
     * @return An Entry Set
     */
    protected Set<Entry<String, Object>> entrySet() {
        return this.map.entrySet();
    }
}

尽管这个方法是 protected 的。