使用 JsonPath 提取 JSON 值
1. 获取和使用 JsonPath 库
可以从中央 Maven 仓库(http://search.maven.org/ )获取 JsonPath。
-
Maven 项目
:如果你熟悉 Maven,可以将以下 XML 片段添加到依赖于 JsonPath 的 Maven 项目的项目对象模型(POM)文件中:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
- 非 Maven 项目 :如果不使用 Maven,可以下载 JsonPath Jar 文件以及它所依赖的所有 Jar 文件,然后将这些 Jar 文件添加到类路径中。最简单的下载方式是访问 https://github.com/jayway/JsonPath/releases ,下载 json-path-2.2.0 - SNAPSHOT - with - dependencies.zip 。
下载并解压 Zip 文件后,会发现 json - path - 2.2.0 - SNAPSHOT - with - dependencies 主目录下有以下子目录:
| 子目录 | 说明 |
| — | — |
| api | 包含基于 Javadoc 的 JsonPath API 文档 |
| lib | 包含为了使用 JsonPath 而需要添加到类路径的 Jar 文件,并非所有情况都需要所有的 Jar 文件,但最好都包含在类路径中 |
| lib - optional | 用于配置 JsonPath 的可选 Jar 文件 |
| source | 包含 JsonPath API 的 Java 源代码的 Jar 文件 |
编译访问 JsonPath 的 Java 源代码时,类路径中只需包含 json - path - 2.2.0 - SNAPSHOT.jar:
javac -cp json-path-2.2.0-SNAPSHOT.jar source file
运行访问 JsonPath 的应用程序时,使用以下命令行:
java -cp accessors-smart-1.1.jar;asm-5.0.3.jar;json-path-2.2.0-SNAPSHOT.jar;json-smart-2.2.1.jar;slf4j-api-1.7.16.jar;tapestry-json-5.4.0.jar;. main classfile
2. 探索 JsonPath 库
JsonPath 库被组织成多个包,通常会与
com.jayway.jsonpath
包及其类型进行交互。以下是提取 JSON 对象值和使用谓词过滤项的相关内容。
3. 从 JSON 对象中提取值
com.jayway.jsonpath
包提供了
JsonPath
类作为使用 JsonPath 库的入口点。以下是一个示例代码:
import java.util.HashMap;
import java.util.List;
import com.jayway.jsonpath.JsonPath;
public class JsonPathDemo {
public static void main(String[] args) {
String json =
"{" +
" \"store\":" +
" {" +
" \"book\":" +
" [" +
" {" +
" \"category\": \"reference\"," +
" \"author\": \"Nigel Rees\"," +
" \"title\": \"Sayings of the Century\"," +
" \"price\": 8.95" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"Evelyn Waugh\"," +
" \"title\": \"Sword of Honour\"," +
" \"price\": 12.99" +
" }" +
" ]," +
" \"bicycle\":" +
" {" +
" \"color\": \"red\"," +
" \"price\": 19.95" +
" }" +
" }" +
"}";
JsonPath path = JsonPath.compile("$.store.book[1]");
HashMap books = path.read(json);
System.out.println(books);
List<Object> authors = JsonPath.read(json, "$.store.book[*].author");
System.out.println(authors);
String author = JsonPath.read(json, "$.store.book[1].author");
System.out.println(author);
}
}
上述代码中,
main()
方法的操作步骤如下:
1. 声明一个基于字符串的 JSON 对象,并将其引用赋值给变量
json
。
2. 调用
JsonPath.compile(String jsonPath, Predicate... filters)
静态方法来编译 JsonPath 表达式,以提高性能,并将编译结果作为
JsonPath
对象返回。
3. 调用
<T> T read(String json)
泛型方法,该方法在之前编译的
JsonPath
实例上调用,接收基于字符串的 JSON 对象作为参数,并将编译后的 JsonPath 表达式应用于该参数。
4. 也可以使用
JsonPath
的静态
read()
方法,例如
<T> T read(String json, String jsonPath, Predicate... filters)
,该方法为
jsonPath
参数创建一个新的
JsonPath
对象并将其应用于
json
字符串。
编译和运行上述代码的命令如下:
javac -cp json-path-2.2.0-SNAPSHOT.jar JsonPathDemo.java
java -cp accessors-smart-1.1.jar;asm-5.0.3.jar;json-path-2.2.0-SNAPSHOT.jar;json-smart-2.2.1.jar;slf4j-api-1.7.16.jar;tapestry-json-5.4.0.jar;. JsonPathDemo
预期输出如下:
{category=fiction, author=Evelyn Waugh, title=Sword of Honour, price=12.99}
["Nigel Rees","Evelyn Waugh"]
Evelyn Waugh
4. 使用谓词过滤项
JsonPath 支持使用过滤器将从 JSON 文档中提取的节点限制为符合谓词(布尔表达式)指定条件的节点。可以使用内联谓词、过滤谓词或自定义谓词。
4.1 内联谓词
内联谓词是基于字符串的谓词。以下是一个示例代码:
import java.util.List;
import com.jayway.jsonpath.JsonPath;
public class JsonPathDemo {
public static void main(String[] args) {
String json =
"{" +
" \"store\":" +
" {" +
" \"book\":" +
" [" +
" {" +
" \"category\": \"reference\"," +
" \"author\": \"Nigel Rees\"," +
" \"title\": \"Sayings of the Century\"," +
" \"price\": 8.95" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"Evelyn Waugh\"," +
" \"title\": \"Sword of Honour\"," +
" \"price\": 12.99" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"J. R. R. Tolkien\"," +
" \"title\": \"The Lord of the Rings\"," +
" \"isbn\": \"0-395-19395-8\"," +
" \"price\": 22.99" +
" }" +
" ]," +
" \"bicycle\":" +
" {" +
" \"color\": \"red\"," +
" \"price\": 19.95" +
" }" +
" }" +
"}";
String expr = "$.store.book[?(@.isbn)].title";
List<Object> titles = JsonPath.read(json, expr);
System.out.println(titles);
expr = "$.store.book[?(@.category == 'fiction')].title";
titles = JsonPath.read(json, expr);
System.out.println(titles);
expr = "$..book[?(@.author =~ /.*REES/i)].title";
titles = JsonPath.read(json, expr);
System.out.println(titles);
expr = "$..book[?(@.price > 10 && @.price < 20)].title";
titles = JsonPath.read(json, expr);
System.out.println(titles);
expr = "$..book[?(@.author in ['Nigel Rees'])].title";
titles = JsonPath.read(json, expr);
System.out.println(titles);
expr = "$..book[?(@.author nin ['Nigel Rees'])].title";
titles = JsonPath.read(json, expr);
System.out.println(titles);
}
}
以下是代码中使用的 JsonPath 表达式及其作用:
| 表达式 | 说明 |
| — | — |
|
$.store.book[?(@.isbn)].title
| 返回所有包含
isbn
属性的
book
元素的
title
值 |
|
$.store.book[?(@.category == 'fiction')].title
| 返回所有
category
属性值为
fiction
的
book
元素的
title
值 |
|
$..book[?(@.author =~ /.*REES/i)].title
| 返回所有
author
属性值以
rees
结尾(不区分大小写)的
book
元素的
title
值 |
|
$..book[?(@.price > 10 && @.price < 20)].title
| 返回所有
price
属性值在 10 到 20 之间的
book
元素的
title
值 |
|
$..book[?(@.author in ['Nigel Rees'])].title
| 返回所有
author
属性值为
Nigel Rees
的
book
元素的
title
值 |
|
$..book[?(@.author nin ['Nigel Rees'])].title
| 返回所有
author
属性值不为
Nigel Rees
的
book
元素的
title
值 |
编译和运行上述代码后,预期输出如下:
["The Lord of the Rings"]
["Sword of Honour","The Lord of the Rings"]
["Sayings of the Century"]
["Sword of Honour"]
["Sayings of the Century"]
["Sword of Honour","The Lord of the Rings"]
4.2 过滤谓词
过滤谓词是表示为
Filter
抽象类实例的谓词,
Filter
类实现了
Predicate
接口。以下是创建和使用过滤谓词的示例代码:
import java.util.List;
import com.jayway.jsonpath.Criteria;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.JsonPath;
public class JsonPathDemo {
public static void main(String[] args) {
String json =
"{" +
" \"store\":" +
" {" +
" \"book\":" +
" [" +
" {" +
" \"category\": \"reference\"," +
" \"author\": \"Nigel Rees\"," +
" \"title\": \"Sayings of the Century\"," +
" \"price\": 8.95" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"Evelyn Waugh\"," +
" \"title\": \"Sword of Honour\"," +
" \"price\": 12.99" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"J. R. R. Tolkien\"," +
" \"title\": \"The Lord of the Rings\"," +
" \"isbn\": \"0-395-19395-8\"," +
" \"price\": 22.99" +
" }" +
" ]," +
" \"bicycle\":" +
" {" +
" \"color\": \"red\"," +
" \"price\": 19.95" +
" }" +
" }" +
"}";
Filter filter = Filter.filter(Criteria.where("price").lt(20.00));
String expr = "$['store']['book'][?].title";
List<Object> titles = JsonPath.read(json, expr, filter);
System.out.println(titles);
}
}
创建和使用过滤谓词的步骤如下:
1. 调用
Criteria.where(String key)
静态方法返回一个存储提供的
key
的
Criteria
对象。
2. 调用
Criteria.lt(Object o)
静态方法返回一个表示
<
运算符的
Criteria
对象,该运算符用于标识与
key
值进行比较的值。
3. 将
Criteria
对象传递给
Filter.filter(Predicate predicate)
方法创建
Filter
对象。
4. 在路径中插入
?
占位符表示过滤谓词。
5. 将过滤谓词传递给
read()
方法。
编译和运行上述代码后,预期输出如下:
["Sayings of the Century","Sword of Honour"]
4.3 自定义谓词
自定义谓词是由实现
Predicate
接口的类创建的谓词。以下是创建和使用自定义谓词的示例代码:
import java.util.List;
import java.util.Map;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Predicate;
public class JsonPathDemo {
public static void main(String[] args) {
String json =
"{" +
" \"store\":" +
" {" +
" \"book\":" +
" [" +
" {" +
" \"category\": \"reference\"," +
" \"author\": \"Nigel Rees\"," +
" \"title\": \"Sayings of the Century\"," +
" \"price\": 8.95" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"Evelyn Waugh\"," +
" \"title\": \"Sword of Honour\"," +
" \"price\": 12.99" +
" }," +
" {" +
" \"category\": \"fiction\"," +
" \"author\": \"J. R. R. Tolkien\"," +
" \"title\": \"The Lord of the Rings\"," +
" \"isbn\": \"0-395-19395-8\"," +
" \"price\": 22.99" +
" }" +
" ]," +
" \"bicycle\":" +
" {" +
" \"color\": \"red\"," +
" \"price\": 19.95" +
" }" +
" }" +
"}";
Predicate expensiveBooks =
new Predicate() {
@Override
public boolean apply(Predicate.PredicateContext ctx) {
String value = ctx.item(Map.class).get("price").toString();
return Float.valueOf(value) > 20.00;
}
};
String expr = "$.store.book[?]";
List<Map<String, Object>> titles = JsonPath.read(json, expr, expensiveBooks);
System.out.println(titles);
}
}
创建和使用自定义谓词的步骤如下:
1. 实例化一个实现
Predicate
接口的类,并重写
boolean apply(Predicate.PredicateContext ctx)
方法。
2. 在
apply()
方法中,根据
PredicateContext
提供的信息判断当前项是否符合条件,返回
true
或
false
。
3. 在路径中插入
?
占位符表示自定义谓词。
4. 将自定义谓词传递给
read()
方法。
编译和运行上述代码后,预期输出如下:
[{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
5. 总结
JsonPath 是一种用于选择和提取 JSON 文档属性值的声明性查询语言,类似于 XPath。可以从中央 Maven 仓库获取 JsonPath,也可以手动下载相关 Jar 文件。主要与
com.jayway.jsonpath
包及其类型进行交互,通过编译和使用 JsonPath 表达式可以从 JSON 对象中提取值,还可以使用谓词过滤项。
6. 练习
以下是一些测试对 JsonPath 理解的练习:
1. 定义 JsonPath。
2. 判断:JsonPath 基于 XPath 2.0 是否正确。
3. 指出表示根 JSON 对象的运算符。
4. 说明可以用哪些符号指定 JsonPath 表达式。
5. 指出表示当前正在由过滤谓词处理的节点的运算符。
6. 判断:JsonPath 的深度扫描运算符(
..
)是否等同于 XPath 的
/
符号。
7. 说明
JsonPath.compile(String jsonPath, Predicate... filters)
静态方法的作用。
8. 指出
<T> T read(String json)
泛型方法返回 JSON 对象属性名及其值时的返回类型。
9. 识别三种谓词类别。
10. 给定 JSON 对象
{ "number": [10, 20, 25, 30] }
,编写一个
JsonPathDemo
应用程序,提取并输出最大值(30)、最小值(10)和平均值(21.25)。
通过这些练习,可以进一步巩固对 JsonPath 的理解和应用能力。
使用 JsonPath 提取 JSON 值
7. 练习答案解析
以下是对前面练习的详细解答:
1.
定义 JsonPath
:JsonPath 是一种声明性查询语言(也称为路径表达式语法),用于选择和提取 JSON 文档的属性值。它是一种简单的语言,具有各种类似于 XPath 的功能,用于构造路径表达式,每个表达式以
$
运算符开头,该运算符标识查询的根元素,对应于 XPath 的
/
符号。
2.
判断:JsonPath 基于 XPath 2.0 是否正确
:答案为错误。JsonPath 虽然有一些功能与 XPath 类似,但它并非基于 XPath 2.0。
3.
指出表示根 JSON 对象的运算符
:表示根 JSON 对象的运算符是
$
。
4.
说明可以用哪些符号指定 JsonPath 表达式
:可以使用
$
表示根元素,
[ ]
用于访问数组元素,
.
用于访问对象属性,
*
作为通配符,
..
作为深度扫描运算符等。
5.
指出表示当前正在由过滤谓词处理的节点的运算符
:表示当前正在由过滤谓词处理的节点的运算符是
@
。
6.
判断:JsonPath 的深度扫描运算符(
..
)是否等同于 XPath 的
/
符号
:答案为错误。JsonPath 的深度扫描运算符
..
用于递归搜索整个 JSON 文档,而 XPath 的
/
符号用于选择根节点或子节点,二者功能不同。
7.
说明
JsonPath.compile(String jsonPath, Predicate... filters)
静态方法的作用
:该方法用于编译一个 JsonPath 表达式,以提高性能,并将编译结果作为
JsonPath
对象返回。
Predicate
可变参数列表允许你指定一个过滤谓词数组,以响应
jsonPath
字符串中的过滤谓词占位符(标识为
?
字符)。
8.
指出
<T> T read(String json)
泛型方法返回 JSON 对象属性名及其值时的返回类型
:该方法可以返回多种类型,当返回 JSON 对象属性名及其值时,通常返回
java.util.LinkedHashMap
类的实例(
java.util.Hashmap
的子类)。
9.
识别三种谓词类别
:三种谓词类别分别是内联谓词、过滤谓词和自定义谓词。
10.
给定 JSON 对象
{ "number": [10, 20, 25, 30] }
,编写一个
JsonPathDemo
应用程序,提取并输出最大值(30)、最小值(10)和平均值(21.25)
:
import java.util.List;
import com.jayway.jsonpath.JsonPath;
public class JsonPathDemo {
public static void main(String[] args) {
String json = "{ \"number\": [10, 20, 25, 30] }";
// 提取最大值
List<Integer> numbers = JsonPath.read(json, "$.number");
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
int sum = 0;
for (int num : numbers) {
if (num > max) {
max = num;
}
if (num < min) {
min = num;
}
sum += num;
}
double average = (double) sum / numbers.size();
System.out.println("最大值: " + max);
System.out.println("最小值: " + min);
System.out.println("平均值: " + average);
}
}
8. 实际应用场景分析
JsonPath 在许多实际场景中都有广泛的应用,以下是一些常见的场景及使用示例:
| 应用场景 | 描述 | 示例代码 |
| — | — | — |
|
API 测试
| 在测试 RESTful API 时,需要从返回的 JSON 响应中提取特定的值进行验证。 |
java String jsonResponse = "{ \"status\": \"success\", \"data\": { \"id\": 123, \"name\": \"John\" } }"; String status = JsonPath.read(jsonResponse, "$.status"); System.out.println("API 状态: " + status);
|
|
数据处理
| 处理大量 JSON 数据时,需要提取特定的字段进行分析或转换。 |
java String jsonData = "{ \"users\": [ { \"name\": \"Alice\", \"age\": 25 }, { \"name\": \"Bob\", \"age\": 30 } ] }"; List<String> names = JsonPath.read(jsonData, "$.users[*].name"); System.out.println("用户姓名: " + names);
|
|
配置文件解析
| 从 JSON 配置文件中提取配置信息。 |
java String jsonConfig = "{ \"database\": { \"host\": \"localhost\", \"port\": 3306 } }"; String host = JsonPath.read(jsonConfig, "$.database.host"); int port = JsonPath.read(jsonConfig, "$.database.port"); System.out.println("数据库主机: " + host); System.out.println("数据库端口: " + port);
|
9. 性能优化建议
在使用 JsonPath 时,为了提高性能,可以考虑以下几点建议:
-
编译表达式
:如果需要多次使用相同的 JsonPath 表达式,建议使用
JsonPath.compile()
方法进行编译,避免重复解析表达式。例如:
JsonPath path = JsonPath.compile("$.store.book[1]");
for (int i = 0; i < 100; i++) {
HashMap books = path.read(json);
// 处理结果
}
-
减少不必要的扫描
:尽量使用精确的路径表达式,避免使用深度扫描运算符
..进行不必要的全局搜索。 - 合理使用谓词 :在使用谓词过滤时,确保谓词逻辑简洁高效,避免复杂的条件判断。
10. 总结与展望
JsonPath 作为一种强大的 JSON 查询语言,为处理和操作 JSON 数据提供了便捷的方式。通过使用 JsonPath,可以轻松地从 JSON 对象中提取所需的值,并使用谓词进行灵活的过滤。在实际应用中,要根据具体的场景选择合适的使用方式,并注意性能优化。
未来,随着 JSON 在各种领域的广泛应用,JsonPath 可能会不断发展和完善,支持更多的功能和更复杂的查询需求。同时,与其他技术的集成也将更加紧密,为开发者提供更高效的开发体验。
以下是使用 JsonPath 的基本流程图:
graph TD;
A[获取 JSON 数据] --> B[编译 JsonPath 表达式];
B --> C[应用表达式提取值];
C --> D[使用谓词过滤结果];
D --> E[处理提取结果];
通过学习和掌握 JsonPath,开发者可以更加高效地处理 JSON 数据,提升开发效率和质量。希望本文能帮助你更好地理解和应用 JsonPath。
超级会员免费看
836

被折叠的 条评论
为什么被折叠?



