序列化 之 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

⚠️ 注意:只有 ObjectNodeArrayNode 支持修改操作。


六、构建 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,若路径不存在则返回 MissingNodeisMissingNode() == 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
时区/日期格式 配置 ObjectMapperJavaTimeModule

十、总结:何时使用 JsonNode?

场景 推荐
JSON 结构固定、已知 ✅ 使用 POJO(@RequestBody MyDto
JSON 结构动态、嵌套不确定 ✅ 使用 JsonNode
需要部分读取/修改 JSON JsonNode
高性能解析超大 JSON ❌ 改用流式解析(JsonParser
需要强类型安全 ✅ POJO

💡 经验法则

  • 内部系统、API 契约明确 → 用 POJO。
  • 对接第三方、Webhook、配置文件 → 用 JsonNode

通过 JsonNode,你可以灵活处理任何 JSON 结构,是 Java 中处理动态 JSON 的强大工具。

posted @ 2026-01-26 19:14  蓝迷梦  阅读(8)  评论(0)    收藏  举报