用于读取 JSON 文档的 Java DSL。
Jayway JsonPath 是 Stefan Goessner JsonPath 实现的 Java 端口。
JsonPath 可在中央 Maven 存储库中获取。 Maven 用户将其添加到您的 POM 中。
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
如果您需要帮助,请在 Stack Overflow 上提问。标记问题“jsonpath”和“java”。
JsonPath 表达式始终引用 JSON 结构,就像 XPath 表达式与 XML 文档结合使用一样。 JsonPath 中的“根成员对象”始终被称为$
,无论它是对象还是数组。
JsonPath 表达式可以使用点符号
$.store.book[0].title
或括号符号
$['store']['book'][0]['title']
操作员 | 描述 |
---|---|
$ | 要查询的根元素。这将启动所有路径表达式。 |
@ | 当前节点正在由过滤谓词处理。 |
* | 通配符。在任何需要名称或数字的地方都可用。 |
.. | 深度扫描。任何需要名称的地方都可以使用。 |
.<name> | 点标记的孩子 |
['<name>' (, '<name>')] | 带括号的子项或子项 |
[<number> (, <number>)] | 数组索引或索引 |
[start:end] | 数组切片运算符 |
[?(<expression>)] | 过滤表达式。表达式的计算结果必须为布尔值。 |
可以在路径的尾部调用函数 - 函数的输入是路径表达式的输出。函数的输出由函数本身决定。
功能 | 描述 | 输出类型 |
---|---|---|
min() | 提供数字数组的最小值 | 双倍的 |
max() | 提供数字数组的最大值 | 双倍的 |
avg() | 提供数字数组的平均值 | 双倍的 |
stddev() | 提供数字数组的标准差值 | 双倍的 |
length() | 提供数组的长度 | 整数 |
sum() | 提供数字数组的总和值 | 双倍的 |
keys() | 提供属性键(终端波形符~ 的替代方案) | Set<E> |
concat(X) | 提供带有新项目的路径输出的串联版本 | 喜欢输入 |
append(X) | 将项目添加到 json 路径输出数组 | 喜欢输入 |
first() | 提供数组的第一项 | 取决于数组 |
last() | 提供数组的最后一项 | 取决于数组 |
index(X) | 提供索引为X的数组的项,如果X为负数,则从后往前取 | 取决于数组 |
过滤器是用于过滤数组的逻辑表达式。典型的过滤器是[?(@.age > 18)]
,其中@
表示当前正在处理的项目。可以使用逻辑运算符&&
和||
创建更复杂的过滤器。字符串文字必须用单引号或双引号括起来( [?(@.color == 'blue')]
或[?(@.color == "blue")]
)。
操作员 | 描述 |
---|---|
== | 左等于右(注意1不等于'1') |
!= | 左不等于右 |
< | 左边小于右边 |
<= | 左小于或等于右 |
> | 左边大于右边 |
>= | 左大于或等于右 |
=~ | left 匹配正则表达式 [?(@.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 | left(数组或字符串)应该为空 |
给定 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
}
Json路径 | 结果 |
---|---|
$.store.book[*].author | 所有书籍的作者 |
$..author | 所有作者 |
$.store.* | 所有的东西,包括书籍和自行车 |
$.store..price | 一切的价格 |
$..book[2] | 第三本书 |
$..book[-2] | 倒数第二本书 |
$..book[0,1] | 前两本书 |
$..book[:2] | 从索引 0(含)到索引 2(不含)的所有书籍 |
$..book[1:2] | 从索引 1(含)到索引 2(不含)的所有书籍 |
$..book[-2:] | 最后两本书 |
$..book[2:] | 从索引 2(含)到最后的所有书籍 |
$..book[?(@.isbn)] | 所有带有 ISBN 号的图书 |
$.store.book[?(@.price < 10)] | 店内所有书籍均价低于10 |
$..book[?(@.price <= $['expensive'])] | 店里所有不“贵”的书 |
$..book[?(@.author =~ /.*REES/i)] | 所有匹配正则表达式的书籍(忽略大小写) |
$..* | 给我一切 |
$..book.length() | 图书数量 |
使用 JsonPath 最简单、最直接的方法是通过静态读取 API。
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
如果您只想读一次,那也可以。如果您还需要读取其他路径,这不是正确的方法,因为每次调用 JsonPath.read(...) 时都会解析文档。为了避免这个问题,你可以先解析 json。
String json = "..." ;
Object document = Configuration . defaultConfiguration (). jsonProvider (). parse ( json );
String author0 = JsonPath . read ( document , "$.store.book[0].author" );
String author1 = JsonPath . read ( document , "$.store.book[1].author" );
JsonPath 还提供了流畅的 API。这也是最灵活的一种。
String json = "..." ;
ReadContext ctx = JsonPath . parse ( json );
List < String > authorsOfBooksWithISBN = ctx . read ( "$.store.book[?(@.isbn)].author" );
List < Map < String , Object >> expensiveBooks = JsonPath
. using ( configuration )
. parse ( json )
. read ( "$.store.book[?(@.price > 10)]" , List . class );
在 java 中使用 JsonPath 时,了解您期望的结果类型很重要。 JsonPath 将自动尝试将结果转换为调用者期望的类型。
//Will throw an java.lang.ClassCastException
List < String > list = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
//Works fine
String author = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
在评估路径时,您需要了解路径何时是definite
的概念。如果路径包含以下内容,则该路径是indefinite
:
..
- 深度扫描操作员?(<expression>)
- 一个表达式[<number>, <number> (, <number>)]
- 多个数组索引Indefinite
路径始终返回一个列表(由当前 JsonProvider 表示)。
默认情况下,MappingProvider SPI 提供一个简单的对象映射器。这允许您指定所需的返回类型,并且 MappingProvider 将尝试执行映射。在下面的示例中,演示了Long
和Date
之间的映射。
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
如果您将 JsonPath 配置为使用JacksonMappingProvider
、 GsonMappingProvider
或JakartaJsonProvider
您甚至可以将 JsonPath 输出直接映射到 POJO 中。
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
要获取完整的泛型类型信息,请使用 TypeRef。
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
在 JsonPath 中创建过滤谓词有三种不同的方法。
内联谓词是在路径中定义的谓词。
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
您可以使用&&
和||
组合多个谓词[?(@.price < 10 && @.category == 'fiction')]
, [?(@.category == 'reference' || @.price > 10)]
。
你可以使用!
否定谓词[?(!(@.price < 10 && @.category == 'fiction'))]
。
可以使用 Filter API 构建谓词,如下所示:
import static com . jayway . jsonpath . JsonPath . parse ;
import static com . jayway . jsonpath . Criteria . where ;
import static com . jayway . jsonpath . Filter . filter ;
...
...
Filter cheapFictionFilter = filter (
where ( "category" ). is ( "fiction" ). and ( "price" ). lte ( 10D )
);
List < Map < String , Object >> books =
parse ( json ). read ( "$.store.book[?]" , cheapFictionFilter );
注意到占位符了?
对于路径中的过滤器。当提供多个过滤器时,它们的应用顺序是占位符的数量必须与提供的过滤器的数量相匹配。您可以在一个过滤操作中指定多个谓词占位符[?, ?]
,两个谓词必须匹配。
过滤器还可以与“OR”和“AND”组合
Filter fooOrBar = filter (
where ( "foo" ). exists ( true )). or ( where ( "bar" ). exists ( true )
);
Filter fooAndBar = filter (
where ( "foo" ). exists ( true )). and ( where ( "bar" ). exists ( true )
);
第三种选择是实现您自己的谓词
Predicate booksWithISBN = new Predicate () {
@ Override
public boolean apply ( PredicateContext ctx ) {
return ctx . item ( Map . class ). containsKey ( "isbn" );
}
};
List < Map < String , Object >> books =
reader . read ( "$.store.book[?].isbn" , List . class , booksWithISBN );
在 Goessner 实现中, JsonPath 可以返回Path
或Value
。 Value
是默认值,也是上面所有示例返回的值。如果您希望获得我们查询所命中的元素的路径,则可以通过一个选项来实现。
Configuration conf = Configuration . builder ()
. options ( Option . AS_PATH_LIST ). build ();
List < String > pathList = using ( conf ). parse ( json ). read ( "$..author" );
assertThat ( pathList ). containsExactly (
"$['store']['book'][0]['author']" ,
"$['store']['book'][1]['author']" ,
"$['store']['book'][2]['author']" ,
"$['store']['book'][3]['author']" );
该库提供了设置值的可能性。
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
创建配置时,有一些选项标志可以更改默认行为。
DEFAULT_PATH_LEAF_TO_NULL
此选项使 JsonPath 对于缺失的叶子返回 null。考虑以下 json
[
{
"name" : "john" ,
"gender" : "male"
} ,
{
"name" : "ben"
}
]
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
String gender0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
//PathNotFoundException thrown
String gender1 = JsonPath . using ( conf ). parse ( json ). read ( "$[1]['gender']" );
Configuration conf2 = conf . addOptions ( Option . DEFAULT_PATH_LEAF_TO_NULL );
//Works fine
String gender0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
//Works fine (null is returned)
String gender1 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[1]['gender']" );
ALWAYS_RETURN_LIST
此选项将 JsonPath 配置为返回列表,即使路径是definite
。
Configuration conf = Configuration . defaultConfiguration ();
//ClassCastException thrown
List < String > genders0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
Configuration conf2 = conf . addOptions ( Option . ALWAYS_RETURN_LIST );
//Works fine
List < String > genders0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
SUPPRESS_EXCEPTIONS
此选项可确保路径评估不会传播任何异常。它遵循以下简单规则:
ALWAYS_RETURN_LIST
存在,将返回一个空列表ALWAYS_RETURN_LIST
不存在,则返回 null需要属性
此选项将 JsonPath 配置为在评估indefinite
路径时需要在路径中定义的属性。
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
List < String > genders = JsonPath . using ( conf ). parse ( json ). read ( "$[*]['gender']" );
Configuration conf2 = conf . addOptions ( Option . REQUIRE_PROPERTIES );
//PathNotFoundException thrown
List < String > genders = JsonPath . using ( conf2 ). parse ( json ). read ( "$[*]['gender']" );
JsonPath 附带五个不同的 JsonProvider:
仅当初始化应用程序时才应更改所示的配置默认值。强烈建议不要在运行时进行更改,尤其是在多线程应用程序中。
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
。
Jakarta EE 9 JSON-P (JSR-342) 和 JSON-B (JSR-367) 提供商都期望至少使用 Java 8,并且需要在应用程序运行时类路径上兼容的 JSON API 实现(例如 Eclipse Glassfish 和 Eclipse Yasson); Java EE 应用程序容器也可以提供此类实现。另请注意,Apache Johnzon 尚未与 Jakarta EE 9 规范类路径兼容,如果选择 JSON-B 映射提供程序,则还必须配置和使用 JSON-P 提供程序。
Jakarta EE 9 规范的 JSON 处理和数据绑定(映射)的一个特点是 Json 数组和对象在完全解析或写入后就保持不变。为了尊重 API 规范,但允许 JsonPath 通过添加、设置/放置、替换和删除操作修改 Json 文档, JakartaJsonProvider
必须使用可选的true
参数进行初始化:
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(启用可变 Json 数组和对象)JsonProvider jsonProvider = new JakartaJsonProvider()
(默认,严格的 JSON-P API 合规性)无论初始化模式如何,都支持使用 JsonPath 的所有查找和读取操作。默认模式还需要更少的内存并且性能更高。
在 JsonPath 2.1.0 中引入了新的 Cache SPI。这允许 API 使用者以适合其需求的方式配置路径缓存。第一次访问时必须配置缓存,否则抛出JsonPathException。 JsonPath 附带两个缓存实现
com.jayway.jsonpath.spi.cache.LRUCache
(默认,线程安全)com.jayway.jsonpath.spi.cache.NOOPCache
(无缓存)如果您想实现自己的缓存,API 很简单。
CacheProvider . setCache ( new Cache () {
//Not thread safe simple cache
private Map < String , JsonPath > map = new HashMap < String , JsonPath >();
@ Override
public JsonPath get ( String key ) {
return map . get ( key );
}
@ Override
public void put ( String key , JsonPath jsonPath ) {
map . put ( key , jsonPath );
}
});