<think>
我们需要处理JsonNode中的数组。通常,在使用Jackson库处理JSON时,JsonNode可以表示数组(即ArrayNode)。以下我们将展示如何遍历、修改和过滤JsonNode数组。
主要步骤:
1. 将JsonNode转换为ArrayNode(如果它是数组类型)
2. 遍历数组中的每个元素
3. 修改数组元素
4. 过滤数组
5. 创建新的数组
为了处理JsonNode数组,我们需要使用Jackson库。确保项目中包含以下依赖(Maven):
```xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version> <!-- 使用最新版本 -->
</dependency>
```
## 示例代码
以下是一个完整的示例,展示如何读取JSON数组,遍历、修改、过滤和创建新的数组:
```java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.Iterator;
public class JsonNodeArrayExample {
public static void main(String[] args) throws IOException {
// JSON字符串示例
String jsonArrayStr = "[ " +
"{\"name\": \"Alice\", \"age\": 25}," +
"{\"name\": \"Bob\", \"age\": 30}," +
"{\"name\": \"Charlie\", \"age\": 35}" +
"]";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonArrayStr);
// 1. 检查是否为数组
if (rootNode.isArray()) {
ArrayNode arrayNode = (ArrayNode) rootNode;
// 2. 遍历数组
System.out.println("=== 原始数组 ===");
for (JsonNode node : arrayNode) {
System.out.println(node);
}
// 3. 修改数组:给每个人的年龄加1
System.out.println("\n=== 修改后的数组(年龄加1) ===");
for (JsonNode node : arrayNode) {
if (node.isObject()) {
ObjectNode objNode = (ObjectNode) node;
int age = objNode.get("age").asInt();
objNode.put("age", age + 1);
}
System.out.println(node);
}
// 4. 过滤数组:只保留年龄大于等于31的人
Iterator<JsonNode> it = arrayNode.iterator();
while (it.hasNext()) {
JsonNode node = it.next();
if (node.get("age").asInt() < 31) {
it.remove();
}
}
System.out.println("\n=== 过滤后(年龄>=31) ===");
System.out.println(arrayNode);
// 5. 创建新的数组
ArrayNode newArray = mapper.createArrayNode();
ObjectNode person1 = mapper.createObjectNode();
person1.put("name", "David");
person1.put("age", 40);
newArray.add(person1);
newArray.addAll(arrayNode); // 将过滤后的数组加入新数组
System.out.println("\n=== 新数组 ===");
System.out.println(newArray);
}
}
}
```
## 代码解释
### 1. 解析JSON数组
```java
JsonNode rootNode = mapper.readTree(jsonArrayStr);
```
使用ObjectMapper将JSON字符串解析为JsonNode。如果JSON字符串是一个数组,则rootNode将是一个ArrayNode。
### 2. 遍历数组
```java
for (JsonNode node : arrayNode) {
System.out.println(node);
}
```
因为ArrayNode实现了Iterable接口,可以直接使用增强for循环遍历。
### 3. 修改数组元素
```java
ObjectNode objNode = (ObjectNode) node;
int age = objNode.get("age").asInt();
objNode.put("age", age + 1);
```
将每个元素转换为ObjectNode(因为元素是对象),然后修改age字段的值。
### 4. 过滤数组
```java
Iterator<JsonNode> it = arrayNode.iterator();
while (it.hasNext()) {
JsonNode node = it.next();
if (node.get("age").asInt() < 31) {
it.remove();
}
}
```
使用迭代器遍历数组,并通过迭代器的remove方法移除不符合条件的元素(年龄小于31)。
### 5. 创建新数组
```java
ArrayNode newArray = mapper.createArrayNode();
ObjectNode person1 = mapper.createObjectNode();
person1.put("name", "David");
person1.put("age", 40);
newArray.add(person1);
newArray.addAll(arrayNode);
```
使用ObjectMapper创建ArrayNode和ObjectNode,然后添加元素。
## 输出示例
```
=== 原始数组 ===
{"name":"Alice","age":25}
{"name":"Bob","age":30}
{"name":"Charlie","age":35}
=== 修改后的数组(年龄加1) ===
{"name":"Alice","age":26}
{"name":"Bob","age":31}
{"name":"Charlie","age":36}
=== 过滤后(年龄>=31) ===
[{"name":"Bob","age":31},{"name":"Charlie","age":36}]
=== 新数组 ===
[{"name":"David","age":40},{"name":"Bob","age":31},{"name":"Charlie","age":36}]
```
## 高级操作:使用Stream API处理ArrayNode
如果你使用Java 8+,可以使用Stream API来处理ArrayNode(需要先转换为Stream):
```java
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import java.util.stream.StreamSupport;
// 将ArrayNode转换为Stream<JsonNode>
ArrayNode arrayNode = ...;
StreamSupport.stream(arrayNode.spliterator(), false)
.filter(node -> node.get("age").asInt() >= 30)
.forEach(System.out::println);
// 或者收集回ArrayNode
ArrayNode filteredArray = JsonNodeFactory.instance.arrayNode();
StreamSupport.stream(arrayNode.spliterator(), false)
.filter(node -> node.get("age").asInt() >= 30)
.forEach(filteredArray::add);
```
## 注意事项
- 在修改ArrayNode时,注意如果JsonNode是从不可变配置中解析出来的,可能会抛出异常(默认是可变的)。
- 使用迭代器删除元素是安全的,但在遍历过程中修改元素内容(如修改字段)也是允许的。
## 结论
JsonNode(特别是ArrayNode)提供了灵活的方法来处理JSON数组。通过结合Jackson库和Java的集合操作,我们可以高效地遍历、修改、过滤和创建JSON数组。
---
### §§相关问题§§:
1. 如何将Java对象数组转换为JsonNode数组?
2. 如何使用JsonPath处理复杂的JSON数组查询?
3. 如何合并多个JsonNode数组?
4. 如何处理嵌套的JSON数组(数组中的数组)?
</think>
# 深入解析 JsonNode 数组处理
Jackson 库的 `JsonNode` 是处理 JSON 数据的核心类,尤其在处理数组时非常强大。下面我将详细介绍如何使用 `JsonNode` 高效处理 JSON 数组。
## 核心处理代码示例
```java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class JsonNodeArrayProcessor {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
// 示例 JSON 数组
String jsonArray = """
[
{"id": 1, "name": "Alice", "age": 30, "active": true},
{"id": 2, "name": "Bob", "age": 25, "active": false},
{"id": 3, "name": "Charlie", "age": 35, "active": true}
]""";
// 1. 解析 JSON 数组
JsonNode rootNode = mapper.readTree(jsonArray);
System.out.println("原始数组: " + rootNode);
// 2. 遍历数组元素
System.out.println("\n遍历元素:");
for (JsonNode node : rootNode) {
String name = node.get("name").asText();
int age = node.get("age").asInt();
System.out.println(name + " (" + age + ")");
}
// 3. 过滤数组
System.out.println("\n过滤活跃用户:");
ArrayNode activeUsers = mapper.createArrayNode();
rootNode.forEach(user -> {
if (user.get("active").asBoolean()) {
activeUsers.add(user);
}
});
System.out.println(activeUsers);
// 4. 修改数组内容
System.out.println("\n添加新属性:");
ObjectNode updatedNode = (ObjectNode) rootNode.get(0);
updatedNode.put("department", "Engineering");
System.out.println(rootNode);
// 5. 转换为 Java 集合
System.out.println("\n转换为 Java List:");
List<User> userList = mapper.convertValue(rootNode, new TypeReference<List<User>>() {});
userList.forEach(System.out::println);
}
static class User {
private int id;
private String name;
private int age;
private boolean active;
// Getters, setters, toString...
}
}
```
## JsonNode 数组处理详解
### 1. 解析数组
```java
JsonNode rootNode = mapper.readTree(jsonArray);
```
- 使用 `ObjectMapper` 的 `readTree()` 方法直接将 JSON 字符串解析为 `JsonNode`
- 当 JSON 以 `[` 开头时,解析结果将是 `ArrayNode` 类型
### 2. 遍历数组
```java
for (JsonNode node : rootNode) {
// 处理每个元素
}
```
- `ArrayNode` 实现了 `Iterable<JsonNode>` 接口,可直接用于 for-each 循环
- 使用 `get(fieldName)` 访问对象属性
- 使用 `asText()`、`asInt()` 等方法获取值
### 3. 过滤数组元素
```java
ArrayNode filtered = mapper.createArrayNode();
rootNode.forEach(element -> {
if (condition) {
filtered.add(element);
}
});
```
- 使用 `forEach()` 方法遍历数组
- 通过 `createArrayNode()` 创建新数组
- 使用 `add()` 方法添加符合条件的元素
### 4. 修改数组内容
```java
ObjectNode firstElement = (ObjectNode) rootNode.get(0);
firstElement.put("newField", "value");
```
- 使用 `get(index)` 获取特定位置的元素
- 将 `JsonNode` 转换为 `ObjectNode` 进行修改
- 使用 `put()` 添加或修改字段
### 5. 数组转换为 Java 集合
```java
List<User> users = mapper.convertValue(rootNode, new TypeReference<List<User>>() {});
```
- 使用 `convertValue()` 方法直接转换为 Java 集合
- 需要指定泛型类型 `TypeReference`
- POJO 类应有无参构造函数和 getter/setter
## 高级操作技巧
### 1. 流式处理数组
```java
List<String> names = StreamSupport.stream(rootNode.spliterator(), false)
.map(node -> node.get("name").asText())
.collect(Collectors.toList());
```
### 2. 查找特定元素
```java
Optional<JsonNode> bob = StreamSupport.stream(rootNode.spliterator(), false)
.filter(node -> "Bob".equals(node.get("name").asText()))
.findFirst();
```
### 3. 合并多个数组
```java
ArrayNode merged = mapper.createArrayNode();
merged.addAll((ArrayNode) array1);
merged.addAll((ArrayNode) array2);
```
### 4. 数组排序
```java
List<JsonNode> sorted = StreamSupport.stream(rootNode.spliterator(), false)
.sorted(Comparator.comparingInt(node -> node.get("age").asInt()))
.collect(Collectors.toList());
ArrayNode sortedArray = mapper.createArrayNode();
sorted.forEach(sortedArray::add);
```
## 实用工具类
```java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class JsonArrayUtils {
private static final ObjectMapper mapper = new ObjectMapper();
// 查找单个元素
public static Optional<JsonNode> findFirst(ArrayNode array, Predicate<JsonNode> condition) {
return StreamSupport.stream(array.spliterator(), false)
.filter(condition)
.findFirst();
}
// 过滤数组
public static ArrayNode filter(ArrayNode array, Predicate<JsonNode> condition) {
ArrayNode result = mapper.createArrayNode();
StreamSupport.stream(array.spliterator(), false)
.filter(condition)
.forEach(result::add);
return result;
}
// 映射转换
public static <T> List<T> mapToObjects(ArrayNode array, Class<T> targetType) {
return StreamSupport.stream(array.spliterator(), false)
.map(node -> mapper.convertValue(node, targetType))
.collect(Collectors.toList());
}
// 分组统计
public static ArrayNode groupBy(ArrayNode array, String field) {
ArrayNode result = mapper.createArrayNode();
StreamSupport.stream(array.spliterator(), false)
.collect(Collectors.groupingBy(node -> node.get(field).asText()))
.forEach((key, nodes) -> {
ObjectNode group = mapper.createObjectNode();
group.put(field, key);
group.set("count", mapper.valueToTree(nodes.size()));
group.set("items", mapper.valueToTree(nodes));
result.add(group);
});
return result;
}
}
```
## 性能优化建议
1. **避免重复解析**:解析 JSON 是相对昂贵的操作,尽量只解析一次
2. **使用树模型替代数据绑定**:当数据结构变化频繁时,树模型更灵活
3. **批量处理**:对于大型数组,使用流式处理避免内存溢出
4. **自定义序列化**:对于性能关键场景,实现自定义 `JsonSerializer`
5. **使用 JSON Pointer**:快速定位特定元素
```java
JsonNode specificNode = rootNode.at("/2/name"); // 获取第三个元素的name字段
```
## 常见问题解决方案
### 问题1:处理嵌套数组
```json
[
{
"id": 1,
"name": "Department A",
"employees": [
{"name": "Alice", "role": "Manager"},
{"name": "Bob", "role": "Developer"}
]
}
]
```
处理代码:
```java
JsonNode departments = mapper.readTree(json);
departments.forEach(dept -> {
ArrayNode employees = (ArrayNode) dept.get("employees");
employees.forEach(emp -> {
String name = emp.get("name").asText();
String role = emp.get("role").asText();
// 处理员工数据...
});
});
```
### 问题2:处理混合类型数组
```json
[
"text",
42,
{"name": "object"},
[1, 2, 3]
]
```
处理代码:
```java
rootNode.forEach(element -> {
if (element.isTextual()) {
// 处理文本
} else if (element.isNumber()) {
// 处理数字
} else if (element.isObject()) {
// 处理对象
} else if (element.isArray()) {
// 处理嵌套数组
}
});
```
### 问题3:数组分页
```java
public ArrayNode paginate(ArrayNode array, int page, int size) {
int fromIndex = page * size;
int toIndex = Math.min(fromIndex + size, array.size());
ArrayNode pageNodes = mapper.createArrayNode();
for (int i = fromIndex; i < toIndex; i++) {
pageNodes.add(array.get(i));
}
return pageNodes;
}
```
### 问题4:数组差异比较
```java
public ArrayNode diff(ArrayNode array1, ArrayNode array2) {
Set<JsonNode> set1 = new HashSet<>();
StreamSupport.stream(array1.spliterator(), false).forEach(set1::add);
ArrayNode diff = mapper.createArrayNode();
StreamSupport.stream(array2.spliterator(), false)
.filter(node -> !set1.contains(node))
.forEach(diff::add);
return diff;
}
```
---
###