用於讀取 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 );
}
});