序列化 之 jackson(四)JsonNode
Jackson 的 JsonNode 是其 树模型(Tree Model) API 的核心类,用于以无模式(schema-less) 的方式解析、遍历、修改和生成 JSON 数据。相比绑定到 Java POJO(数据绑定模型),JsonNode 更适合处理结构未知、动态或嵌套深度不确定的 JSON。
一、基本概念
JsonNode:抽象基类,代表 JSON 中的任意节点(对象、数组、字符串、数字、布尔、null)。- 常用子类:
ObjectNode:JSON 对象{},可增删改字段。ArrayNode:JSON 数组[],可增删元素。TextNode/BooleanNode/NumericNode/NullNode:对应基本类型。
✅ 所有操作都通过
JsonNode接口进行,无需强转(但修改时需转为ObjectNode/ArrayNode)。
二、核心依赖(Maven)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version> <!-- 或使用 Spring Boot 管理 -->
</dependency>
Spring Boot 项目通常已包含。
三、创建 JsonNode(解析 JSON)
1. 从 JSON 字符串解析
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"name": "Alice",
"age": 30,
"active": true,
"tags": ["java", "json"],
"address": {"city": "Beijing", "zip": "100000"}
}
""";
JsonNode rootNode = mapper.readTree(json);
2. 从文件/URL/InputStream 解析
JsonNode node = mapper.readTree(new File("data.json"));
JsonNode node = mapper.readTree(new URL("https://api.example.com/data"));
四、读取 JsonNode(安全访问)
✅ 推荐:使用 .get() + .isXxx() 判断 + .asXxx() 转换
// 获取字段(不存在返回 null)
JsonNode nameNode = rootNode.get("name");
if (nameNode != null && nameNode.isTextual()) {
String name = nameNode.asText(); // "Alice"
}
// 布尔值
boolean active = rootNode.get("active").asBoolean(false); // 默认 false
// 数字(自动转换)
int age = rootNode.get("age").asInt(); // 30
double ageDouble = rootNode.get("age").asDouble();
// 嵌套对象
JsonNode address = rootNode.get("address");
String city = address.get("city").asText(); // "Beijing"
// 数组
JsonNode tags = rootNode.get("tags");
if (tags.isArray()) {
for (JsonNode tag : tags) {
System.out.println(tag.asText());
}
}
🔒 安全访问工具方法(避免 NPE)
// 自定义工具类示例
public static String safeText(JsonNode node, String fieldName) {
JsonNode field = node.get(fieldName);
return field != null && field.isTextual() ? field.asText() : null;
}
// 使用
String name = safeText(rootNode, "name");
五、修改 JsonNode(需转为 ObjectNode / ArrayNode)
1. 修改对象字段
// 转为可修改的 ObjectNode
ObjectNode objNode = (ObjectNode) rootNode;
// 添加/替换字段
objNode.put("name", "Bob");
objNode.put("score", 95.5);
objNode.putNull("temp"); // 设置 null
// 删除字段
objNode.remove("age");
// 添加嵌套对象
ObjectNode newAddr = objNode.putObject("newAddress");
newAddr.put("country", "China");
2. 修改数组
ArrayNode tagsArray = (ArrayNode) rootNode.get("tags");
tagsArray.add("spring"); // 追加
tagsArray.insert(0, "backend"); // 插入到索引 0
tagsArray.set(1, "updated"); // 替换索引 1
tagsArray.remove(2); // 删除索引 2
⚠️ 注意:只有
ObjectNode和ArrayNode支持修改操作。
六、构建 JsonNode(从零创建)
ObjectMapper mapper = new ObjectMapper();
// 创建根对象
ObjectNode root = mapper.createObjectNode();
// 添加基本字段
root.put("id", 1001);
root.put("name", "Charlie");
root.put("verified", true);
// 添加数组
ArrayNode hobbies = root.putArray("hobbies");
hobbies.add("reading").add("coding");
// 添加嵌套对象
ObjectNode contact = root.putObject("contact");
contact.put("email", "charlie@example.com");
contact.put("phone", "13800138000");
// 输出 JSON 字符串
String json = mapper.writeValueAsString(root);
System.out.println(json);
输出:
{
"id":1001,
"name":"Charlie",
"verified":true,
"hobbies":["reading","coding"],
"contact":{"email":"charlie@example.com","phone":"13800138000"}
}
七、高级用法
1. 路径查询(JsonPointer)
// 标准 JSON Pointer 语法(RFC 6901)
JsonNode city = rootNode.at("/address/city");
JsonNode firstTag = rootNode.at("/tags/0");
if (!city.isMissingNode()) {
System.out.println(city.asText()); // "Beijing"
}
at()永远不返回 null,若路径不存在则返回MissingNode(isMissingNode() == true)。
2. 与 POJO 互转
// JsonNode → POJO
User user = mapper.treeToValue(rootNode, User.class);
// POJO → JsonNode
JsonNode node = mapper.valueToTree(user);
3. 合并/深拷贝
// 深拷贝
JsonNode copy = rootNode.deepCopy();
// 合并两个 ObjectNode(后者覆盖前者)
ObjectNode merged = rootNode.deepCopy();
((ObjectNode) merged).setAll(anotherObjectNode);
八、在 Spring MVC 中使用
接收 JsonNode
@PostMapping("/update")
public ResponseEntity<String> update(@RequestBody JsonNode payload) {
String id = payload.get("id").asText();
boolean enabled = payload.get("enabled").asBoolean();
// 处理逻辑...
return ResponseEntity.ok("Updated");
}
返回 JsonNode
@GetMapping("/config")
public JsonNode getConfig() {
ObjectNode config = mapper.createObjectNode();
config.put("timeout", 5000);
config.put("retry", 3);
return config;
}
Spring 会自动序列化
JsonNode为 JSON 响应。
九、常见陷阱与最佳实践
| 问题 | 解决方案 |
|---|---|
NullPointerException |
始终检查 node != null 或使用 at() |
| 类型转换错误 | 使用 isTextual() / isBoolean() 等判断 |
| 无法修改节点 | 确保转为 ObjectNode / ArrayNode |
| 性能问题(大 JSON) | 考虑使用流式 API(JsonParser) |
| 时区/日期格式 | 配置 ObjectMapper 的 JavaTimeModule |
十、总结:何时使用 JsonNode?
| 场景 | 推荐 |
|---|---|
| JSON 结构固定、已知 | ✅ 使用 POJO(@RequestBody MyDto) |
| JSON 结构动态、嵌套不确定 | ✅ 使用 JsonNode |
| 需要部分读取/修改 JSON | ✅ JsonNode |
| 高性能解析超大 JSON | ❌ 改用流式解析(JsonParser) |
| 需要强类型安全 | ✅ POJO |
💡 经验法则:
- 内部系统、API 契约明确 → 用 POJO。
- 对接第三方、Webhook、配置文件 → 用
JsonNode。
通过 JsonNode,你可以灵活处理任何 JSON 结构,是 Java 中处理动态 JSON 的强大工具。
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/19535251

浙公网安备 33010602011771号