导读:本期聚焦于石川澪创作的《如何从 JSONArray 中提取每个 JSONObject 并逐个处理?》,敬请观看详情。从接口返回的 JSON 数组里拿到的往往是一个 JSONArray,它的元素并不总是同一种类型,直接强转成 JSONObject 容易踩 ClassCastException 的坑。这篇内容围绕 org.json 和 fastjson 两个常见库,说明如何安全地遍历 JSONArray、判断每个元素是否为 JSONObject,再通过 getJSONObject 或 cast 取出对象做后续字段解析。文中会对比普通 for 循环、迭代器以及 Java 8 Stream 三种写法,并演示如何在循环里处理嵌套对象、跳过 JSONObject.NULL 和异常项。同时补充 opt 系列方法的容错技巧,避免因为某个字段缺失导致整个批次解析中断。

接口返回的 JSON 数组在 Java 里通常会被解析成 JSONArray,例如 [{"id":1,"name":"Tom"},{"id":2,"name":"Jerry"}]。如果直接遍历并强制把每个元素转换成 JSONObject,代码虽然能跑,却隐藏着类型不匹配的风险。因为 JSONArray 内部存储的元素类型是 Object,它可以是 JSONObject、JSONArray、字符串、数字、布尔值,甚至是 JSONObject.NULL。真正可靠的做法是先判断每个元素的实际类型,再取出 JSONObject 做字段解析。

如何从 JSONArray 中提取每个 JSONObject 并逐个处理?

一、先厘清 JSONArray 的元素类型和取出方式

JSONArray 在 org.json 和 fastjson 中都是一个有序容器,实现了 Iterable 接口,但内部维护的元素类型声明为 Object。也就是说,当你调用 get(int index) 时,拿到的并不是一个已经确定好的 JSONObject,而是一个可能为任意类型的对象。这个设计本身是合理的,因为 JSON 数组本来就可以包含混合类型,例如 ["hello", 123, {"key":"value"}]。

下面这段代码演示了 JSONArray 中可能同时存在哪些类型。可以看到,同一个数组里既有 JSONObject,也有字符串、数字和 JSONObject.NULL。如果代码不做任何判断直接写 (JSONObject) jsonArray.get(i),在处理到字符串或数字元素时就会抛出 ClassCastException。

import org.json.JSONArray;
import org.json.JSONObject;

public class JsonArrayDemo {
    public static void main(String[] args) {
        JSONArray jsonArray = new JSONArray();
        jsonArray.put(new JSONObject().put("name", "Tom").put("age", 20));
        jsonArray.put("plain string");
        jsonArray.put(123);
        jsonArray.put(JSONObject.NULL);

        for (int i = 0; i < jsonArray.length(); i++) {
            Object item = jsonArray.get(i);
            System.out.println("index=" + i + ", type=" + item.getClass().getName());
        }
    }
}

因此,逐个处理 JSONArray 的核心不是“如何取出来”,而是“如何安全地判断并取出 JSONObject”。只要在类型判断上做足功夫,后续的字段解析就能避免很多低级错误。不同的 JSON 库在 API 命名上略有差异,比如 fastjson 的 JSONArray 同样提供 getJSONObject(int index) 方法,但它内部并不会自动判断元素类型,遇到非 JSONObject 元素时同样会抛出异常。

二、用 for 循环配合 instanceof 安全提取

最直观也最稳妥的方式是使用普通 for 循环,先通过 get 方法取出 Object,再使用 instanceof 判断它是否为 JSONObject。只有判断通过后才进行强制转换,否则跳过该元素或记录日志。这种方式对运行时的类型变化有很好的容错性,也是大多数后端解析场景的默认选择。

下面的示例读取一个标准的用户 JSON 数组,并在循环内提取每个 JSONObject 的 name 和 age 字段。注意这里使用 optStringoptInt 而不是 getStringgetInt,因为 opt 系列方法在字段不存在时返回默认值,而不会抛出 JSONException,这样可以避免因为个别对象缺字段导致整个批次中断。

import org.json.JSONArray;
import org.json.JSONObject;

public class ExtractJsonObjects {
    public static void main(String[] args) {
        String jsonText = "[{\"name\":\"Tom\",\"age\":20},{\"name\":\"Jerry\",\"age\":25},{\"name\":\"Spike\",\"age\":30}]";
        JSONArray jsonArray = new JSONArray(jsonText);

        for (int i = 0; i < jsonArray.length(); i++) {
            Object item = jsonArray.get(i);
            if (item instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) item;
                String name = jsonObject.optString("name", "unknown");
                int age = jsonObject.optInt("age", 0);
                System.out.println("User: " + name + ", Age: " + age);
            } else {
                System.out.println("Skip non-object item at index " + i);
            }
        }
    }
}

这种写法虽然多了一行类型判断,但可读性很高,团队成员一眼就能看懂边界条件。尤其当 JSON 数据来自外部系统、结构不受控制时,显式的 instanceof 检查就是第一道防线。如果确定数组里的元素全部是 JSONObject,也可以使用 getJSONObject(i) 直接获取,但前提是数据来源足够可靠,否则不建议省略判断。

三、用迭代器和 Stream 让遍历代码更简洁

JSONArray 实现了 Iterable 接口,因此可以直接使用增强 for 循环遍历。增强 for 循环在字节码层面仍然会调用迭代器,但代码更加简洁,适合数组元素数量不大、处理逻辑简单的场景。下面示例先遍历每个元素,判断类型后放入一个专门的 List,再集中处理。

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class IterateJsonArray {
    public static void main(String[] args) {
        JSONArray jsonArray = new JSONArray("[{\"name\":\"Tom\"},{\"name\":\"Jerry\"},123]");

        List<JSONObject> objectList = new ArrayList<JSONObject>();
        for (Object item : jsonArray) {
            if (item instanceof JSONObject) {
                objectList.add((JSONObject) item);
            }
        }

        objectList.forEach(obj -> {
            String name = obj.optString("name");
            System.out.println(name);
        });
    }
}

如果你习惯使用 Java 8 的函数式风格,还可以借助 StreamSupport 将 JSONArray 转成 Stream,然后通过 filter 和 map 完成类型过滤与转换。这种写法适合在提取之后还要进行排序、分组或映射等链式操作的场景。需要注意的是,org.json 的 JSONArray 本身并没有直接提供 stream() 方法,需要手动构造 Stream。

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class StreamJsonArray {
    public static void main(String[] args) {
        JSONArray jsonArray = new JSONArray("[{\"name\":\"Tom\"},{\"name\":\"Jerry\"},123]");

        List<JSONObject> users = StreamSupport.stream(jsonArray.spliterator(), false)
                .filter(item -> item instanceof JSONObject)
                .map(item -> (JSONObject) item)
                .collect(Collectors.toList());

        users.forEach(user -> System.out.println(user.optString("name")));
    }
}

虽然 Stream 写法看起来更高级,但它会引入额外的对象创建和迭代开销。如果只是简单地逐一处理,增强 for 循环或普通 for 循环已经足够。选择哪种方式,更多取决于团队规范以及处理逻辑的复杂程度,而不是一味追求函数式写法。

四、处理嵌套对象与 NULL 值的几个关键点

实际项目中的 JSON 很少只有一层结构,字段里经常包含嵌套对象、数组,或者直接就是 null。例如一个用户对象里可能包含 address 对象,而 address 里又有 city 字段。如果某一层的对象缺失,直接用 getJSONObject 就可能得到 null,后续继续调用方法会抛出 NullPointerException。因此,取嵌套对象时应当使用 optJSONObject 并判断返回值是否为 null。

import org.json.JSONArray;
import org.json.JSONObject;

public class NestedJsonDemo {
    public static void main(String[] args) {
        String jsonText = "[{\"user\":{\"name\":\"Tom\",\"address\":{\"city\":\"Beijing\"}},\"score\":null}]";
        JSONArray jsonArray = new JSONArray(jsonText);

        for (int i = 0; i < jsonArray.length(); i++) {
            Object item = jsonArray.get(i);
            if (!(item instanceof JSONObject)) {
                continue;
            }
            JSONObject root = (JSONObject) item;
            JSONObject user = root.optJSONObject("user");
            if (user != null) {
                JSONObject address = user.optJSONObject("address");
                String city = address == null ? "" : address.optString("city", "");
                String score = root.isNull("score") ? "no score" : root.optString("score");
                System.out.println("City: " + city + ", Score: " + score);
            }
        }
    }
}

这里还涉及一个容易混淆的概念:JSON 里的 null 对应的是 JSONObject.NULL,它不是 Java 的 null。在 org.json 中,调用 isNull("score") 可以判断该字段是否为 JSON 的 null 值。如果直接使用 optString("score"),当字段为 JSON null 时,返回的可能是字符串 "null",而不是预期的 null 或默认值,这会让后续逻辑产生误判。

对于 fastjson 用户,处理逻辑类似,但 API 略有不同。fastjson 的 JSONArray 提供 getJSONObjectgetObject,如果元素类型不匹配,前者会抛异常,后者可以配合 instanceof 使用。无论使用哪个库,核心思路都是:先判断元素类型,再安全地向下解析,遇到可能缺失的字段一律走 opt 或判空分支。这样写出来的解析代码才能稳定应对真实世界中结构多变、字段不完备的 JSON 数据。

简而言之,从 JSONArray 中提取 JSONObject 并不是一个复杂操作,难的是让它足够健壮。类型判断、字段容错、嵌套判空,这三件事做到位,批量处理 JSON 数据时就能少踩很多坑。

JSONArrayJSONObject遍历处理修改时间:2026-08-25 23:55:50

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。