1. 概述

在 Java 中处理 JSON 数据时,经常需要访问嵌套键值。流行的 JSON 处理库 Jackson 提供了一种便捷方式:使用 findValue() 方法实现这一目标。

本教程将深入探讨如何使用 findValue() 方法检索嵌套键值。

2. 理解 JSON 中的嵌套结构

JSON 对象可能包含复杂的 嵌套结构,这使得访问深层嵌套的值变得棘手:

{
  "user": {
    "id": 1,
    "name": "John Doe",
    "contact": {
      "email": "john.doe@example.com",
      "phone": "123-456-7890"
    }
  }
}

在这个 JSON 中,email 键嵌套在 contact 对象内,而 contact 又嵌套在 user 对象中。

3. 使用 findValue() 方法

Jackson 中的 findValue() 方法允许我们在 JSON 树中搜索特定键并获取其关联值。

首先,**通过 ObjectMapper 将 JSON 字符串转换为 JsonNode**,创建 JSON 数据的树形表示:

String jsonString = "{ \"user\": { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"email\": \"john.doe@example.com\", \"phone\": \"123-456-7890\" } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

然后使用 findValue() 方法在 rootNode 中搜索 email 键:

String email = rootNode.findValue("email").asText();
assertEquals("john.doe@example.com", email);

这里使用 asText() 将包含 emailJsonNode 转换为字符串。

4. 优雅处理缺失键

处理 JSON 时常会遇到键缺失的情况。当键不存在时,findValue() 方法返回 null

考虑以下缺少 email 键的 JSON:

String jsonString = "{ \"user\": { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"phone\": \"123-456-7890\" } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);
JsonNode emailNode = rootNode.findValue("email");

本例中 findValue("email") 返回 null,因为 JSON 中没有 email 键。可通过断言验证:

assertNull(emailNode);

⚠️ 踩坑提醒:直接调用 asText() 会抛出 NullPointerException,务必先检查返回值是否为 null!

5. 在数组中使用 findValue() 方法

findValue() 方法同样适用于 JSON 数组:

{
  "users": [
    { "id": 1, "name": "John Doe", "contact": { "email": "john.doe@example.com" } },
    { "id": 2, "name": "Jane Doe", "contact": { "email": "jane.doe@example.com" } }
  ]
}

解析 JSON 字符串:

String jsonString = "{ \"users\": [ { \"id\": 1, \"name\": \"John Doe\", \"contact\": { \"email\": \"john.doe@example.com\" } }, { \"id\": 2, \"name\": \"Jane Doe\", \"contact\": { \"email\": \"jane.doe@example.com\" } } ] }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

使用 findValues() 搜索所有 email 键的出现位置:

List<String> emails = rootNode.findValues("email")
    .stream()
    .map(JsonNode::asText)
    .collect(Collectors.toList());
assertThat(emails)
  .containsExactly("john.doe@example.com", "jane.doe@example.com");

这里 findValues() 返回包含所有邮箱地址的 JsonNode 列表,然后将其转换为字符串并收集到列表中。

6. 处理深层嵌套键

如果需要在特定路径获取字段值,而非在 JSON 结构中按名称查找键,可以使用 at() 方法。

考虑以下 JSON:

{
  "organization": {
    "department": {
      "team": {
        "lead": {
          "name": "Alice",
          "contact": {
            "email": "alice.leader@example.com"
          }
        }
      }
    }
  }
}

获取团队负责人的邮箱地址,首先解析 JSON 字符串:

String jsonString = "{ \"organization\": { \"department\": { \"team\": { \"lead\": { \"name\": \"Alice\", \"contact\": { \"email\": \"alice.leader@example.com\" } } } } } }";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

使用 at() 方法导航嵌套结构:

String email = rootNode.at("/organization/department/team/lead/contact/email").asText();
assertEquals("alice.leader@example.com", email);

提供给 at() 方法的路径是 JSON Pointer,这是一种使用字符串语法导航 JSON 文档的标准方式。

简单粗暴的路径规则

  • 路径以 / 开头
  • 每级用 / 分隔
  • 无需担心中间节点是否存在(不存在会返回缺失节点)

7. 结论

Jackson 的 findValue() 方法为获取 JSON 嵌套键值提供了强大而灵活的解决方案。

无论是处理深层嵌套对象还是数组,findValue() 都能简化流程并提升代码可读性与可维护性。但对于精确定位字段,可结合 JSON 路径使用 at() 方法。

完整源代码和示例可在 GitHub 获取。


原始标题:Using findValue() to Get the Value for a Nested Key in Jackson | Baeldung