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
(不可变设计)。
📌 支持的值类型包括:
String
→JsonString
- 数值类型:
int
,long
,double
,BigDecimal
,BigInteger
→JsonNumber
- 甚至
boolean
和null
底层 createValue()
是重载方法,自动识别类型。
4.4 更新已有键值
更新操作分两步:
- 创建新值
- 调用
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。