1. 概述

本文将带你掌握 如何使用 JSON Pointer 对 JSON 数据进行导航和数据提取

不仅如此,我们还会演示如何通过它完成一些常见的数据操作,比如插入新字段、更新已有键值对等。对于后端开发中频繁处理 JSON 的场景,这套 API 能让你的操作更简洁、精准,避免手动遍历结构的“脏活”。

2. 依赖配置

要使用 JSON Pointer,首先需要引入 javax.json 的依赖。这是 Java EE 中 JSON Processing(JSR 374)标准的一部分。

pom.xml 中添加:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.1.2</version>
</dependency>

✅ 推荐使用 Maven 管理依赖,版本 1.1.2 稳定且广泛支持。

⚠️ 注意:该库属于 Java EE 标准,若你使用的是 Spring Boot 等现代框架,可能需要额外确认模块路径兼容性(尤其是 JDK 11+ 环境)。

3. JSON Pointer 简介

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,由 Douglas Crockford 提出。虽然语法源自 JavaScript,但它是语言无关的纯文本格式,广泛用于前后端通信。

JSON Pointer(定义于 RFC 6901)是 JSON Processing 1.1 API 的核心特性之一。它的作用类似于 XML 中的 XPath —— ✅ 用一个字符串路径精准定位 JSON 中的某个节点。

举个例子:

/books/0/title

就能直接指向第一个书名。

通过 JSON Pointer,你可以:

  • ✅ 读取指定路径的值
  • ✅ 判断某路径是否存在
  • ✅ 插入、更新或删除节点

接下来我们通过实际代码演示这些操作。

4. 数据访问与操作

我们假设有一个 books.json 文件,内容如下:

{
    "library": "My Personal Library",
    "books": [
        { "title":"Title 1", "author":"Jane Doe" },
        { "title":"Title 2", "author":"John Doe" }
    ]
}

首先,加载并解析 JSON 文件为 JsonStructure 对象:

JsonReader reader = Json.createReader(new FileReader("books.json"));
JsonStructure jsonStructure = reader.read();
reader.close();

这个 jsonStructure 就是我们后续所有操作的基础对象。

4.1 获取指定路径的数据

使用 Json.createPointer() 创建指针,再调用 getValue() 提取数据。

示例:获取 library 字段值:

JsonPointer jsonPointer = Json.createPointer("/library");
JsonString jsonString = (JsonString) jsonPointer.getValue(jsonStructure);
System.out.println(jsonString.getString());

输出:

My Personal Library

⚠️ 注意:路径必须以 / 开头,这是 JSON Pointer 的语法要求,否则会抛异常。

从数组中取值?也很简单,用索引即可:

JsonPointer jsonPointer = Json.createPointer("/books/1");
JsonObject jsonObject = (JsonObject) jsonPointer.getValue(jsonStructure);
System.out.println(jsonObject.toString());

输出:

{"title":"Title 2", "author":"John Doe"}

✅ 索引从 0 开始,/books/1 表示第二本书。

4.2 判断路径是否存在

不想取值只想确认某个字段是否存在?用 containsValue() 方法:

JsonPointer jsonPointer = Json.createPointer("/library");
boolean found = jsonPointer.containsValue(jsonStructure);
System.out.println(found); // 输出 true

✅ 返回 true 表示该路径存在且有值,否则为 false。适合做条件判断前的“探路”。

4.3 插入新键值对

想动态添加字段?比如记录图书总数:

JsonPointer jsonPointer = Json.createPointer("/total");
JsonNumber jsonNumber = Json.createValue(2);
jsonStructure = jsonPointer.add(jsonStructure, jsonNumber);
System.out.println(jsonStructure);

输出:

{
    "library": "My Personal Library",
    "total": 2,
    "books": [
        { "title":"Title 1", "author":"Jane Doe" },
        { "title":"Title 2", "author":"John Doe" }
    ]
}

add() 方法会创建新节点并返回新的 JsonStructure(不可变设计)。

📌 支持的值类型包括:

  • StringJsonString
  • 数值类型:int, long, double, BigDecimal, BigIntegerJsonNumber
  • 甚至 booleannull

底层 createValue() 是重载方法,自动识别类型。

4.4 更新已有键值

更新操作分两步:

  1. 创建新值
  2. 调用 replace() 替换

示例:把 total 从 2 改成 5:

JsonPointer jsonPointer = Json.createPointer("/total");
JsonNumber jsonNumberNewValue = Json.createValue(5);
jsonStructure = jsonPointer.replace(jsonStructure, jsonNumberNewValue);
System.out.println(jsonStructure);

输出:

{
    "library": "My Personal Library",
    "total": 5,
    "books": [
        { "title":"Title 1", "author":"Jane Doe" },
        { "title":"Title 2", "author":"John Doe" }
    ]
}

⚠️ 如果路径不存在,replace() 会抛出 JsonException。建议先用 containsValue() 判断,避免踩坑。

4.5 删除某个键

删除更简单,直接调用 remove()

JsonPointer jsonPointer = Json.createPointer("/library");
jsonStructure = jsonPointer.remove(jsonStructure);
System.out.println(jsonStructure);

输出:

{
    "total": 5,
    "books": [
        { "title":"Title 1", "author":"Jane Doe" },
        { "title":"Title 2", "author":"John Doe" }
    ]
}

✅ 删除后返回新的 JsonStructure,原对象不受影响(不可变性)。

📌 注意:删除数组元素也是支持的,例如 /books/0 可以删除第一本书。

4.6 获取完整 JSON 内容

有时候你想“全量导出”,可以用空路径 ""

JsonPointer jsonPointer = Json.createPointer("");
JsonObject jsonObject = (JsonObject) jsonPointer.getValue(jsonStructure);
System.out.println(jsonObject.toString());

✅ 这会返回整个 JSON 根对象。虽然看起来多余,但在泛型处理或封装工具类时很实用。

5. 总结

JSON Pointer 提供了一套简单粗暴但非常高效的 API,用于操作 JSON 结构:

  • ✅ 路径式访问,类似 XPath,语义清晰
  • ✅ 支持增删改查,覆盖常见场景
  • ✅ 基于标准 JSR 374,无需引入额外框架
  • ✅ 不可变设计,线程安全

虽然功能不如 Jackson 或 Gson 强大,但在轻量级场景下,尤其是需要精确路径操作时,它是原生 API 中的“利器”。

📌 完整示例代码已托管至 GitHub: https://github.com/eugenp/tutorials/tree/master/json-modules/json

建议集合,下次处理 JSON 路径操作时直接翻这篇,省得再翻 RFC。


原始标题:Overview of JSON Pointer