JsonPath是用来提取json文档中内容的,跟xpath用来提取html或xml中内容一样,是一种路径表达式。
而这里说的是Java语言中的json-path包,其他语言如JavaScript也是也可以使用JsonPath的,但是相关解析的第三包不一样,下面所用的包是Java语言支持的,但json路径表达式的语法是通用的。
如果使用Gson等进行解析json,提取里面的个别值,代码会很多比较麻烦,而JsonPath只需要一个简单的路径表达式就可以提取到值,代码简洁。
一般情况下,做爬虫的对提取json中内容的需求比较大。
它的官网地址:
JsonPath
它的maven坐标如下:
<
dependency
>
<
groupId
>
com.jayway.jsonpath
</
groupId
>
<
artifactId
>
json-path
</
artifactId
>
<
version
>
2.6.0
</
version
>
</
dependency
>
如果报错
Caused by: java.lang.NoClassDefFoundError: net/minidev/json/writer/JsonReaderI
则需要加上如下依赖:
<
dependency
>
<
groupId
>
net.minidev
</
groupId
>
<
artifactId
>
asm
</
artifactId
>
<
version
>
1.0.2
</
version
>
<
scope
>
test
</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>
net.minidev
</
groupId
>
<
artifactId
>
json-smart
</
artifactId
>
<
version
>
2.2.1
</
version
>
<
scope
>
test
</
scope
>
</
dependency
>
JsonPath中的根成员对象是
$
。JsonPath获取数据有两种表示方法:
-
点表示法:
$.store.book[0].title
-
括号表示法:
$['store']['book'][0]['title']
入门示例如下:
public class Test {
@Test
public void hello() {
String json = "{\"msg\":\"hello world\"}";
String msg = JsonPath.read(json, "$.msg");
System.out.println(msg);
用于测试的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": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.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
"expensive": 10
JsonPath支持的操作符如下表:
操作 | 说明 |
---|
$ | 查询根元素。这将启动所有路径表达式。 |
@ | 当前节点由过滤谓词处理。 |
* | 通配符,必要时可用任何地方的名称或数字。 |
.. | 深层扫描。必要时在任何地方可以使用名称。 |
.<name> | 点,表示子节点 |
['<name>' (, '<name>')] | 括号表示子项 |
[<number> (, <number>)] | 数组索引或索引,索引从0开始 |
[start:end] | 数组切片操作 |
[?(<expression>)] | 过滤表达式。 表达式必须求值为一个布尔值。 |
示例如下:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
List<String> authors = JsonPath.read(json, "$.store.book[*].author");
System.out.println(authors);
@Test
public void test02() {
List<String> authors = JsonPath.read(json, "$..author");
System.out.println(authors);
@Test
public void test03() {
Object obj = JsonPath.read(json, "$.store.*");
System.out.println(obj);
@Test
public void test04() {
List<Float> prices = JsonPath.read(json, "$.store..price");
System.out.println(prices);
@Test
public void test05() {
List<Map<String, Object>> book = JsonPath.read(json, "$..book[2]");
System.out.println(book);
@Test
public void test06() {
List<Map<String, Object>> book = JsonPath.read(json, "$..book[-2]");
System.out.println(book);
@Test
public void test07() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[0,1]");
System.out.println(books);
@Test
public void test08() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[:2]");
System.out.println(books);
@Test
public void test09() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[1:2]");
System.out.println(books);
@Test
public void test10() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[-2:]");
System.out.println(books);
@Test
public void test11() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[2:]");
System.out.println(books);
@Test
public void test12() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[?(@.isbn)]");
System.out.println(books);
@Test
public void test13() {
List<Map<String, Object>> books = JsonPath.read(json, "$..book[?(@.price < 10)]");
System.out.println(books);
JsonPath还支持一些函数,函数可以在路径的尾部调用,函数的输出是路径表达式的输出,该函数的输出是由函数本身所决定的。如下表:
函数 | 描述 | 输出 |
---|
min() | 提供数字数组的最小值 | Double |
max() | 提供数字数组的最大值 | Double |
avg() | 提供数字数组的平均值 | Double |
stddev() | 提供数字数组的标准偏差值 | Double |
length() | 提供数组的长度 | Integer |
sum() | 提供数组中所有原生的总和 | Double |
keys() | 提供所有的键名 | Set |
concat(X) | Provides a concatinated version of the path output with a new item | like input |
append(X) | 向路径表达式的输出数组新增一项 | like input |
示例如下:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
int length = JsonPath.read(json, "$..book.length()");
System.out.println(length);
@Test
public void test02() {
double min = JsonPath.read(json, "$..book..price.min()");
System.out.println(min);
@Test
public void test03() {
double max = JsonPath.read(json, "$..book..price.max()");
System.out.println(max);
@Test
public void test04() {
double avg = JsonPath.read(json, "$..book..price.avg()");
System.out.println(avg);
@Test
public void test05() {
Object obj = JsonPath.read(json, "$..book..price.stddev()");
System.out.println(obj);
@Test
public void test06() {
double sum = JsonPath.read(json, "$..book..price.sum()");
System.out.println(sum);
@Test
public void test07() {
Set<String> set = JsonPath.read(json, "$.store.book[2].keys()");
System.out.println(set);
过滤器是用于筛选数组的逻辑表达式。一个典型的过滤器将是[?(@.age > 18)]
,其中@
表示正在处理的当前项。 可以使用逻辑运算符&&
和||
(分别表示与
和或
)创建更复杂的过滤器。 字符串文字必须用单引号或双引号括起来([?(@.color == 'blue')]
或者 [?(@.color == "blue")]
).
操作符 | 描述 |
---|
== | left等于right(注意1不等于’1’) |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
=~ | 匹配正则表达式,如[?(@.name =~ /foo.*?/i)] |
in | 左边存在于右边,如[?(@.size in ['S', 'M'])] |
nin | 左边不存在于右边 |
subsetof | 左边是一个右边的子集合,如[?(@.sizes subsetof ['S','M','L'])] |
anyof | 左边的集合与右边的集合相交,如[?(@.sizes anyof ['M','L'])] |
noneof | 左右的集合与右边的集合不相交,如[?(@.sizes noneof ['M','L'])] |
size | 左边(数组或字符串)长度应该匹配右边的数值 |
empty | 左边(数组或字符串)为空 |
示例如下:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category=='fiction')]");
System.out.println(books);
@Test
public void test02() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category!='fiction')]");
System.out.println(books);
@Test
public void test03() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.price<10)]");
System.out.println(books);
@Test
public void test04() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.price>10)]");
System.out.println(books);
@Test
public void test05() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.title =~ /.*of the.*/i)]");
System.out.println(books);
@Test
public void test06() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category in ['reference','abc'])]");
System.out.println(books);
@Test
public void test07() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.category nin ['reference','abc'])]");
System.out.println(books);
@Test
public void test08() {
String json = "{\"sizes\":[\"S\",\"M\"]}";
Object result = JsonPath.read(json, "$[?(@.sizes subsetof ['S','M','L'])]");
System.out.println(result);
@Test
public void test09() {
String json = "{\"sizes\":[\"S\",\"M\"]}";
Object result = JsonPath.read(json, "$[?(@.sizes anyof ['M','L'])]");
System.out.println(result);
@Test
public void test10() {
String json = "{\"sizes\":[\"S\",\"X\"]}";
Object result = JsonPath.read(json, "$[?(@.sizes noneof ['M','L'])]");
System.out.println(result);
@Test
public void test11() {
List<Map<String, Object>> books = JsonPath.read(json, "$.store.book[?(@.title size 9)]");
System.out.println(books);
上面的语法已经够我们日常使用了,下面是JsonPath的一些扩展使用。
我们上面使用的都是JsonPath.read()
方法读取内容,如果只读取一次那么是可行的,如果想要读取多次,那么这不是一个好的方法,因为该方法每次读取都需要解析整个json文档,耗费性能。
所以如果我们需要读取多个路径,那么我们可以选择先解析整个文档,再解析路径读取内容。
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
long start1 = System.currentTimeMillis();
String author0 = JsonPath.read(json, "$.store.book[0].author");
String author1 = JsonPath.read(json, "$.store.book[1].author");
long end1 = System.currentTimeMillis();
System.out.println(author0 + ", " + author1 + ", " + (end1 - start1));
long start2 = System.currentTimeMillis();
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
String author2 = JsonPath.read(document, "$.store.book[0].author");
String author3 = JsonPath.read(document, "$.store.book[1].author");
long end2 = System.currentTimeMillis();
System.out.println(author2 + ", " + author3 + ", " + (end2 - start2));
核心代码就是:
String json="...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
String author2 = JsonPath.read(document, "$.store.book[0].author");
还可以使用下面这种API,链式配置很灵活:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
ReadContext context = JsonPath.parse(json);
List<String> authors = context.read("$.store.book..author");
System.out.println(authors);
List list = JsonPath
.using(Configuration.defaultConfiguration())
.parse(json)
.read("$.store.book..author", List.class);
System.out.println(list);
在使用JsonPath时,要明确知道路径的结果是什么类型,JsonPath会尝试转换为所期待的类型,如:
List<String> list = JsonPath.parse(json).read("$.store.book[0].author")
String author = JsonPath.parse(json).read("$.store.book[0].author")
在评估路径时,您需要了解路径何时确定的概念。如果路径包含以下内容,则该路径是不确定的:
..
:深层扫描操作?(<expression>)
:表达式[<number>, <number> (, <number>)]
:多个数组索引
不确定的路径总数返回一个列表,可由List接收。
- 简单对象映射
默认情况下,MappingProvider SPI提供了一个简单的对象映射器。 这允许您指定所需的返回类型,MappingProvider将尝试执行映射。如将Long类型的时间戳转换成Date类型的日期:
public class JsonPathTest {
@Test
public void test01() {
String json = "{\"timestamp\":1641519588137}";
Date date = JsonPath.parse(json).read("$.timestamp", Date.class);
System.out.println(date);
- POJO对象映射
如果是简单的实体类映射,如Book
,如下即可:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
System.out.println(book);
class Book {
private String category;
private String author;
private String title;
private String isbn;
private float price;
public Book() {
public Book(String category, String author, String title, String isbn, float price) {
this.category = category;
this.author = author;
this.title = title;
this.isbn = isbn;
this.price = price;
public String getCategory() {
return category;
public void setCategory(String category) {
this.category = category;
public String getAuthor() {
return author;
public void setAuthor(String author) {
this.author = author;
public String getTitle() {
return title;
public void setTitle(String title) {
this.title = title;
public String getIsbn() {
return isbn;
public void setIsbn(String isbn) {
this.isbn = isbn;
public float getPrice() {
return price;
public void setPrice(float price) {
this.price = price;
@Override
public String toString() {
return "Book{" +
"category='" + category + '\'' +
", author='" + author + '\'' +
", title='" + title + '\'' +
", isbin='" + isbn + '\'' +
", price=" + price +
'}';
但如果是更复杂的情况,如List<Book>
,则可以考虑将JsonPath配置为使用JacksonMappingProvider或GsonMappingProvider,这样就能让JsonPath输出直接映射到POJO中。
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new GsonJsonProvider();
private final MappingProvider mappingProvider = new GsonMappingProvider();
@Override
public JsonProvider jsonProvider() {
return jsonProvider;
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
@Override
public MappingProvider mappingProvider() {
return mappingProvider;
});
TypeRef<List<Book>> typeRef = new TypeRef<List<Book>>() {};
List<Book> books = JsonPath.parse(json).read("$.store.book.*", typeRef);
for (Book book : books) {
System.out.println(book);
class Book {
private String category;
private String author;
private String title;
private String isbn;
private float price;
public Book() {
public Book(String category, String author, String title, String isbn, float price) {
this.category = category;
this.author = author;
this.title = title;
this.isbn = isbn;
this.price = price;
public String getCategory() {
return category;
public void setCategory(String category) {
this.category = category;
public String getAuthor() {
return author;
public void setAuthor(String author) {
this.author = author;
public String getTitle() {
return title;
public void setTitle(String title) {
this.title = title;
public String getIsbn() {
return isbn;
public void setIsbn(String isbn) {
this.isbn = isbn;
public float getPrice() {
return price;
public void setPrice(float price) {
this.price = price;
@Override
public String toString() {
return "Book{" +
"category='" + category + '\'' +
", author='" + author + '\'' +
", title='" + title + '\'' +
", isbin='" + isbn + '\'' +
", price=" + price +
'}';
在JsonPath中创建过滤器谓词有三种不同的方法。
内联谓词就是在路径中直接定义的谓词。如:
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?(@.price < 10)]");
其中[?(@.price < 10)]
就是内联谓词,要求过滤书籍价格小于10的书。
还可以使用$$
和||
结合使用多个谓词,即多个过滤条件呈现“与”和“或”的关系,如[?(@.price < 10 && @.category == 'fiction')]
表示过滤价格小于10并且分类为fiction的书籍(多个条件同时满足)、[?(@.category == 'reference' || @.price > 10)]
表示过滤分类为reference或者价格大于10的书籍(只要满足任一条件即可)。
还有一个!
表示“非”,如[?(!(@.price < 10 && @.category == 'fiction'))]
表示价格既不小于10分类也不是fiction的书籍。
谓词可以使用Filter API构建,如下所示:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Filter bookFilter = Filter.filter(
Criteria.where("category").is("fiction").and("price").lte(10)
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", bookFilter);
System.out.println(books);
- 路径表达式
$.store.book[?]
中使用了占位符?
来表示路径中的过滤器。 - 当提供多个过滤器时,它们按照占位符数量与提供的过滤器数量相匹配的顺序应用。
- 可以在一个过滤器操作
[?,?]
中指定多个谓词占位符,这两个谓词都必须匹配。 - 过滤器可以使用
.or()
、.and()
、.exists()
来表示“或”、“与”、“非”。
还可以实现自己的谓词,进行更复杂的过滤。
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Predicate booksPredicate = new Predicate() {
@Override
public boolean apply(PredicateContext context) {
return context.item(Map.class).get("author").toString().length() == 10;
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", List.class, booksPredicate);
System.out.println(books);
默认情况下,JsonPath是返回值的,但也可以要求它返回匹配元素的完整路径。如下:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Configuration conf = Configuration.builder().options(Option.AS_PATH_LIST).build();
List<String> pathList = JsonPath.using(conf).parse(json).read("$..author");
System.out.println(pathList);
选项创建配置时,有几个可以改变默认行为的选项标志。
此选项使JsonPath对于缺少的叶子返回null,如下的json:
"name" : "john",
"gender" : "male"
"name" : "ben"
获取第二个对象中的gender
,如果按照默认配置项则会抛出异常,我们希望它不存在时能返回null,那么可以设置DEFAULT_PATH_LEAF_TO_NULL
。
public class JsonPathTest {
@Test
public void test01() {
String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";
Configuration conf = Configuration.defaultConfiguration();
String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
System.out.println(gender0 + ", " + gender1);
@Test
public void test02() {
String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";
Configuration conf2 = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
System.out.println(gender0 + ", " + gender1);
此选项将JsonPath配置为返回列表,即使路径是确定的。
如$[0]['gender']
的结果是一个字符串,我们可以使用String
来接收它,但如果我们想要使用List<String>
来接收这单个字符串,那么就需要配置ALWAYS_RETURN_LIST
选项。
public class JsonPathTest {
@Test
public void test01() {
String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";
Configuration conf = Configuration.defaultConfiguration();
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
System.out.println(genders0);
@Test
public void test02() {
String json = "[{\"name\":\"john\",\"gender\":\"male\"},{\"name\":\"ben\"}]";
Configuration conf = Configuration.defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST);
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
System.out.println(genders0);
该选项确保不会从路径评估传播异常。 遵循这些简单的规则:
- 如果选项ALWAYS_RETURN_LIST存在,将返回一个空列表
- 如果选项ALWAYS_RETURN_LIST不存在返回null
JsonPath附带了五个不同的JsonProvider:
JsonSmartJsonProvider
(默认)JacksonJsonProvider
JacksonJsonNodeJsonProvider
GsonJsonProvider
JsonOrgJsonProvider
JakartaJsonProvider
只有在初始化应用程序时,才能更改所演示的配置默认值。强烈反对在运行时进行更改,尤其是在多线程应用程序中。
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
@Override
public JsonProvider jsonProvider() {
return jsonProvider;
@Override
public MappingProvider mappingProvider() {
return mappingProvider;
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
});
注意:JacksonJsonProvider需要com.fasterxml.jackson.core:jackson-databind:2.4.5
,GsonJsonProvider需要在您的类路径上使用com.google.code.gson:gson:2.3.1
,即需要导入相关的jar包才能使用。
使用JacksonJsonProvider或GsonJsonProvider可以直接将JsonPath的输出映射到实体类上,如下例:
public class JsonPathTest {
private 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void test01() {
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new GsonJsonProvider();
private final MappingProvider mappingProvider = new GsonMappingProvider();
@Override
public JsonProvider jsonProvider() {
return jsonProvider;
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
@Override
public MappingProvider mappingProvider() {
return mappingProvider;
});
TypeRef<List<Book>> typeRef = new TypeRef<List<Book>>() {};
List<Book> books = JsonPath.parse(json).read("$.store.book.*", typeRef);
for (Book book : books) {
System.out.println(book);
class Book {
private String category;
private String author;
private String title;
private String isbn;
private float price;
public Book() {
public Book(String category, String author, String title, String isbn, float price) {
this.category = category;
this.author = author;
this.title = title;
this.isbn = isbn;
this.price = price;
public String getCategory() {
return category;
public void setCategory(String category) {
this.category = category;
public String getAuthor() {
return author;
public void setAuthor(String author) {
this.author = author;
public String getTitle() {
return title;
public void setTitle(String title) {
this.title = title;
public String getIsbn() {
return isbn;
public void setIsbn(String isbn) {
this.isbn = isbn;
public float getPrice() {
return price;
public void setPrice(float price) {
this.price = price;
@Override
public String toString() {
return "Book{" +
"category='" + category + '\'' +
", author='" + author + '\'' +
", title='" + title + '\'' +
", isbin='" + isbn + '\'' +
", price=" + price +
'}';
针对使用JsonPath来提取json字符串中的内容然后封装到实体类中,可以做一些简单的封装:
package json;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
* @author lcl100
* @desc JsonPath工具类
* @date 2022-01-10
public class JsonPathUtil {
* JsonPath配置
private final static Configuration configuration = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
* 根据路径表达式读取json中的内容
* @param json 待读取的json字符串
* @param path JsonPath路径表达式
* @return 读取到的结果
private static Object readPath(String json, String path) {
Object result = null;
try {
result = JsonPath.using(configuration).parse(json).read(path);
} catch (Exception ignored) {
return result;
* 根据json模板字符串提取json中对应内容然后填充到类中对应映射字段,提取实体类
* @param json 待提取的json字符串
* @param jsonPath json模板字符串,存储着实体类属性名与json中对应内容的路径关系
* @param clazz 待映射的实体类
* @param <T> 实体类泛型
* @return 从json字符串中提取了内容后并赋值的实体类对象
* @throws InstantiationException 创建实例对象异常
* @throws IllegalAccessException 字段访问权限异常
public static <T> T getJsonObject(String json, String jsonPath, Class<T> clazz) throws InstantiationException, IllegalAccessException {
Map<String, Map<String, String>> arrayMap = new HashMap<>();
Map<String, String> objectMap = new HashMap<>();
jsonPath = jsonPath.trim();
char firstChar = jsonPath.charAt(0);
boolean isArray = false;
if (firstChar == '[') {
isArray = true;
List<Map<String, String>> mapList = (List<Map<String, String>>) readPath(jsonPath, "$");
for (Map<String, String> map : mapList) {
arrayMap.put(map.getOrDefault("NAME", null), map);
} else if (firstChar == '{') {
isArray = false;
objectMap = (Map<String, String>) readPath(jsonPath, "$");
T t = clazz.newInstance();
Class<?> tClass = t.getClass();
Field[] fields = tClass.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
String path = isArray ? (arrayMap.getOrDefault(name, null) != null ? arrayMap.get(name).get("PATH") : null) : objectMap.getOrDefault(name, null);
Object value = readPath(json, path);
if (isArray) {
Matcher matcher = Pattern.compile(arrayMap.getOrDefault(name, null).getOrDefault("REGEXP", null)).matcher(String.valueOf(value));
if (matcher.find()) {
value = matcher.group(1);
setField(field, t, value);
return t;
* 根据json模板字符串提取json中对应内容然后填充到类中对应映射字段,提取实体类集合
* @param json 待提取的json字符串
* @param jsonPath json模板字符串,存储着实体类属性名与json中对应内容的路径关系
* @param clazz 待映射的实体类
* @param <T> 实体类泛型
* @return 赋值后的实体类集合
* @throws InstantiationException 创建实例对象异常
* @throws IllegalAccessException 字段访问权限异常
public static <T> List<T> getJsonList(String json, String jsonPath, Class<T> clazz) throws InstantiationException, IllegalAccessException {
List<T> list = new ArrayList<>();
Map<String, Map<String, String>> arrayMap = new HashMap<>();
Map<String, String> objectMap = new HashMap<>();
boolean isArray = jsonPath.contains("DATA");
if (isArray) {
List<Map<String, String>> mapList = (List<Map<String, String>>) readPath(jsonPath, "$.DATA");
for (int i = 0; i < mapList.size(); i++) {
Map<String, String> map = mapList.get(i);
String name = map.get("NAME");
String path = map.get("PATH");
if (name != null && !"".equals(name.trim()) && path != null && !"".equals(path.trim())) {
arrayMap.put(name, map);
} else {
objectMap = (Map<String, String>) readPath(jsonPath, "$");
String rootPath = (String) readPath(jsonPath, "$.ROOT_LIST");
int length = (int) readPath(json, rootPath + ".length()");
for (int j = 0; j < length; j++) {
T t = clazz.newInstance();
Class<?> tClass = t.getClass();
Field[] fields = tClass.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
Object value;
if (isArray) {
if (arrayMap.getOrDefault(fieldName, null) == null) {
continue;
value = readPath(json, rootPath + "[" + j + "]" + arrayMap.getOrDefault(fieldName, null).get("PATH"));
Matcher matcher = Pattern.compile(arrayMap.getOrDefault(fieldName, null).getOrDefault("REGEXP", null)).matcher(String.valueOf(value));
if (matcher.find()) {
value = matcher.group(1);
} else {
if (objectMap.getOrDefault(fieldName, null) == null) {
continue;
value = readPath(json, rootPath + "[" + j + "]" + objectMap.getOrDefault(fieldName, null));
setField(field, t, value);
list.add(t);
return list;
* 根据字段类型为实例对象上的字段进行赋值
* @param field 待赋值的字段
* @param t 实例对象
* @param value 待赋予的值
* @throws IllegalAccessException 字段访问权限异常
private static void setField(Field field, Object t, Object value) throws IllegalAccessException {
String typeName = field.getType().getTypeName();
field.setAccessible(true);
if (typeName.endsWith("String")) {
field.set(t, String.valueOf(value));
} else if (typeName.endsWith("byte")) {
byte result = 0;
try {
result = Byte.parseByte(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Byte")) {
Byte result = null;
try {
result = Byte.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("short")) {
short result = 0;
try {
result = Short.parseShort(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Short")) {
Short result = null;
try {
result = Short.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("int")) {
int result = 0;
try {
result = Integer.parseInt(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Integer")) {
Integer result = null;
try {
result = Integer.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("long")) {
long result = 0L;
try {
result = Long.parseLong(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Long")) {
Long result = null;
try {
result = Long.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("float")) {
float result = 0F;
try {
result = Float.parseFloat(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Float")) {
Float result = null;
try {
result = Float.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("double")) {
double result = 0;
try {
result = Double.parseDouble(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Double")) {
Double result = null;
try {
result = Double.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("boolean")) {
boolean result = false;
try {
result = Boolean.parseBoolean(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Boolean")) {
Boolean result = null;
try {
result = Boolean.valueOf(value.toString());
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("char")) {
char result = '\u0000';
try {
result = value.toString().charAt(0);
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Character")) {
Character result = null;
try {
result = value.toString().charAt(0);
} catch (Exception ignored) {
field.set(t, result);
} else if (typeName.endsWith("Date")) {
Date result = null;
try {
Matcher timestampMatcher = Pattern.compile("1[\\d]{9}").matcher(value.toString());
if (timestampMatcher.find()) {
result = new Date(Long.parseLong(value.toString()));
} catch (Exception ignored) {
field.set(t, result);
} else {
System.out.println("无法转换的字段类型: " + typeName);
测试类如下:
public class JsonPathUtilTest {
private final 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\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.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}},\"expensive\":10}";
@Test
public void testGetJsonObject() throws InstantiationException, IllegalAccessException {
String jsonPath1 = "{\n" +
" \"category\": \"$.store.book[0].category\",\n" +
" \"author\": \"$.store.book[0].author\",\n" +
" \"title\": \"$.store.book[0].title\",\n" +
" \"isbn\": \"$.store.book[0].isbn\",\n" +
" \"price\": \"$.store.book[0].price\"\n" +
"}";
Book book1 = JsonPathUtil.getJsonObject(json, jsonPath1, Book.class);
System.out.println(book1);
String jsonPath2 = "[\n" +
" {\n" +
" \"NAME\": \"category\",\n" +
" \"PATH\": \"$.store.book[0].category\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"author\",\n" +
" \"PATH\": \"$.store.book[0].author\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"title\",\n" +
" \"PATH\": \"$.store.book[0].title\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"isbn\",\n" +
" \"PATH\": \"$.store.book[0].isbn\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"price\",\n" +
" \"PATH\": \"$.store.book[0].price\",\n" +
" \"REGEXP\": \"(\\\\d+\\\\.\\\\d+)\"\n" +
" }\n" +
"]";
Book book2 = JsonPathUtil.getJsonObject(json, jsonPath1, Book.class);
System.out.println(book2);
@Test
public void testGetJsonList() throws InstantiationException, IllegalAccessException {
String jsonPath1 = "{\n" +
" \"ROOT_LIST\": \"$.store.book\",\n" +
" \"category\": \".category\",\n" +
" \"author\": \".author\",\n" +
" \"title\": \".title\",\n" +
" \"isbn\": \".isbn\",\n" +
" \"price\": \".price\"\n" +
"}";
List<Book> bookList1 = JsonPathUtil.getJsonList(json, jsonPath1, Book.class);
for (Book book : bookList1) {
System.out.println(book);
String jsonPath2 = "{\n" +
" \"ROOT_LIST\": \"$.store.book\",\n" +
" \"DATA\": [\n" +
" {\n" +
" \"NAME\": \"category\",\n" +
" \"PATH\": \".category\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"author\",\n" +
" \"PATH\": \".author\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"title\",\n" +
" \"PATH\": \".title\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"isbn\",\n" +
" \"PATH\": \".isbn\",\n" +
" \"REGEXP\": \"(.*)\"\n" +
" },\n" +
" {\n" +
" \"NAME\": \"price\",\n" +
" \"PATH\": \".price\",\n" +
" \"REGEXP\": \"(\\\\d+\\\\.\\\\d+)\"\n" +
" }\n" +
" ]\n" +
"}";
List<Book> bookList2 = JsonPathUtil.getJsonList(json, jsonPath2, Book.class);
for (Book book : bookList2) {
System.out.println(book);
具体说明请参考:GitHub 文档。
网站地址:在线 JSONPath 测试
可以帮助我们快速判断jsonpath写得是否正确,结果是否是我们期望的结果,而不需要写代码去验证。
介绍fastjson 1.2.0之后的版本支持jsonpath。,可以在java框架中当作json对象查询语言(oql)来使用。常用apipublic class jsonpath {// 求值,静态方法public static object eval(object rootobject, string path);// 求值,静态方法,按需计算,性能更好public static object...
在上面的代码中,$.friends表示JSON根元素中的friends字段,[?(@.age > 30)]表示过滤条件,用于过滤年龄大于30的朋友,name表示要获取的字段名。这里的JsonPath.read()方法返回一个字符串列表,包含匹配条件的所有朋友的名字。在上面的代码中,我们使用$.name路径来获取name字段的值,该路径表示JSON根元素中的name字段。除了读取元素值之外,json-path库还支持一些其他的操作,如过滤、映射、排序等。我们可以使用json-path库来获取其中的某些元素。
I have JSON as a string and a JSONPath as a string. I'd like to query the JSON with the JSON path, getting the resulting JSON as a string.I gather that Jayway's json-path is the standard. The online A...
<dependency>
<groupId>net.minidev</groupId>
<artifactId>asm</artifactId>
<...
背景在一些特殊场景中,可能 一串json有几个甚至上万个节点,那么要去获取里面某一个节点或者说设置某个json指定key的值,那就非常麻烦了,一般我们是通过递归来进行获取,获取后还需要再通过递归进行遍历设置值,所以相当来说非常麻烦。是否有已有现成的工具进行设置呢?注:使用当先请跳转到:注意点进行了解性能问题。jsonPath介绍官网:https://goessner.ne...
public class JSONPath {
// 求值,静态方法
public static Object eval(Object rootObject, String path);
// 计算S...
https://github.com/itguang/gitbook-smile/blob/master/springboot-fastjson/fastjson%E4%B9%8BJSONPath%E4%BD%BF%E7%94%A8.md
1. JSONPath介绍
官网地址: https://github.com/alibaba/fastjson/wiki/JSONPath
fastjson...
Tip注意JSONObject初始化里面记得newLinkedHashMap(),不然会有put进去的数据与输出的数据顺序不一致问题,详情见--->一周工作结束了,这周不太顺利.....周末学习学习,任重而道远......需要的格式(不是上文的对应,只是一个例子)要求被处理的Json文件数据(部分)......
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerAdapter' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet...
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
修改指定字段:
测试数据文件
"visitPlanList".
用来解析多层嵌套的json数据;JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具.
JsonPath有许多编程语言,如Javascript、Python、PHP、Java等
JsonPath提供的json解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的json内容。
JSONPathGitHub:https://github.com/json-path/JsonPath