JSONPath是一個(gè)用于讀取JSON的Java DSL操作庫(kù)
GitHub:https://github.com/json-path/JsonPath
在線調(diào)試:http://jsonpath.herokuapp.com/
JSONPath表達(dá)式用于指定JSON結(jié)構(gòu)元素(或一組元素)的路徑.路徑的表示法可以使用點(diǎn)表示,如下:
$.store.book[0].title
或者括號(hào)表示:
$['store']['book'][0]['title']
**$
**所代表的是JSON根路徑,在使用時(shí)可以忽略.例如$.foobar.name
和foobar.name
所表達(dá)的意思是一樣的,同理$[0].status
和[0].status
也一樣
其他語(yǔ)法元素如下表:
表達(dá)式 | 說(shuō)明 |
---|---|
$ | 根對(duì)象或者數(shù)組 |
.property | 選擇父對(duì)象中指定的屬性 |
[property] | 選擇父對(duì)象中的指定屬性。務(wù)必在屬性名稱周圍加上單引號(hào)。 如果屬性名稱包含空格等特殊字符,或者以A…Za…z_以外的字符開頭,請(qǐng)使用此表示法 |
[n] | 從數(shù)組中選擇第n個(gè)元素。索引從0開始。 |
[index1,index2,…] | 選擇具有指定索引的數(shù)組元素。返回一個(gè)集合列表。 |
..property | 遞歸查找:遞歸搜索指定的屬性名稱,并返回具有此屬性名稱的所有值的數(shù)組。即使只找到一個(gè)屬性,也始終返回一個(gè)列表。 |
* | 通配符選擇對(duì)象或數(shù)組中的所有元素,無(wú)論其名稱或索引如何。例如,address.* 。*表示地址對(duì)象的所有屬性,book [*] 表示書籍?dāng)?shù)組的所有項(xiàng)目 |
[start:end] or [start:] | 從起始索引中選擇數(shù)組元素,最多但不包括結(jié)束索引。如果省略end,則從開始到數(shù)組結(jié)束選擇所有元素。返回一個(gè)列表。 |
[:n] | 選擇數(shù)組的前n個(gè)元素。返回一個(gè)列表。 |
[-n:] | 選擇數(shù)組的最后n個(gè)元素。返回一個(gè)列表。 |
[?expression] | 過(guò)濾表達(dá)式。選擇對(duì)象或數(shù)組中與指定過(guò)濾器匹配的所有元素。返回一個(gè)列表。 |
[(expression)] | 可以使用腳本表達(dá)式代替顯式屬性名稱或索引。一個(gè)例子是[(@.length-1)] ,它選擇數(shù)組中的最后一項(xiàng)。這里,length指的是當(dāng)前數(shù)組的長(zhǎng)度,而不是名為length的JSON字段。 |
@ | 在過(guò)濾器表達(dá)式中用于引用正在處理的當(dāng)前節(jié)點(diǎn)。 |
注意:
過(guò)濾器是用于過(guò)濾數(shù)組的邏輯表達(dá)式。帶有過(guò)濾器的JSONPath表達(dá)式的示例
$.store.book[?(@.price < 10)]
其中@
表示當(dāng)前正在處理的數(shù)組項(xiàng)或?qū)ο蟆_^(guò)濾器也可以使用$
來(lái)引用當(dāng)前對(duì)象之外的屬性
$.store.book[?(@.price < $.expensive)]
只指定屬性名稱的表達(dá)式如[?(@.isbn)]
將匹配具有此屬性的所有項(xiàng)目,無(wú)論值如何
此外,過(guò)濾器支持一下運(yùn)算符
操作符 | 說(shuō)明 |
---|---|
== | 等于,1和'1' 被認(rèn)為是相等的,字符串值必須用單引號(hào)括起來(lái)(不是雙引號(hào)):例如[?(@.color=='red')] |
!= | 不等于。字符串值必須用單引號(hào)括起來(lái)。 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
=~ | 匹配JavaScript正則表達(dá)式,例如[?(@.description=~ /cat.*/i)] 匹配描述以cat開頭的項(xiàng)(不區(qū)分大小寫) |
! | 用于否定,例如[?(!@.isbn)] 匹配沒(méi)有isbn屬性的項(xiàng)目 |
&& | 邏輯AND,用于組合多個(gè)過(guò)濾器表達(dá)式,例如:[?(@.category=='fiction' && @.price < 10 )] |
|| | 邏輯OR,用于組合多個(gè)過(guò)濾器表達(dá)式,例如:[?(@.category=='fiction' || @.price < 10 )] |
目前我們有如下JSON結(jié)構(gòu):
{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "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 }
在下面的例子中,$
符號(hào)是可選的,可以省略掉:
表達(dá)式 | 說(shuō)明 |
---|---|
$.store.* | store對(duì)象下所有的屬性(非遞歸) |
$.store.bicycle.color | 獲取得到color 屬性的值,結(jié)果為red |
$.store..price $..price | 返回所有的price屬性值集合,結(jié)果為[8.95,8.99,22.99,19.95] |
$.store.book[*] $..book[*] | 所有的book集合 |
$..book[*].title | 返回book對(duì)象下的所有標(biāo)題集合 |
$..book[0] | 返回第一個(gè)book集合對(duì)象,結(jié)果為[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}] |
$..book[0].title | 返回第一個(gè)book集合對(duì)象中的title屬性,結(jié)果是Sayings of the Century |
$..book[0,1].title $..book[:2].title | 返回前2個(gè)book集合對(duì)象的title屬性,結(jié)果是[Sayings of the Century, Moby Dick] |
$..book[-1:].title $..book[(@.length-1)].title | 返回最后一個(gè)book對(duì)象的title屬性集合,結(jié)果是[The Lord of the Rings] |
$..book[?(@.author=='J.R.R. Tolkien')].title | 返回book集合中所有的作者等于J.R.R. Tolkien 的title集合,結(jié)果是[The Lord of the Rings] |
$..book[?(@.isbn)] | 返回所有book對(duì)象屬性中含有isbn的屬性,其結(jié)果是books集合 |
$..book[?(!@.isbn)] | 返回book對(duì)象屬性中不包含isbn的屬性,結(jié)果是集合 |
$..book[?(@.price < 10)] | 返回所有book對(duì)象屬性中price 屬性小于10的對(duì)象集合 |
$..book[?(@.price > $.expensive)] | 返回所有book對(duì)象屬性中price 屬性值大于expensive 值的對(duì)象集合 |
$..book[?(@.author =~ /.*Tolkien/i)] | 返回所有book對(duì)象屬性中的author屬性是以Tolkien 結(jié)尾(不區(qū)分大小寫)的屬性對(duì)象集合 |
$..book[?(@.category == 'fiction' || @.category == 'reference')] | 返回book對(duì)象屬性中category 等于fiction 的或者等于reference 的對(duì)象集合 |
$..* | 根目錄下的JSON結(jié)構(gòu)的所有成員(子對(duì)象,單個(gè)屬性值,數(shù)組項(xiàng))組合成一個(gè)數(shù)組。 |
JSONPath查詢不僅可以返回單個(gè)元素,還可以返回匹配元素的列表。
例如如下JSON結(jié)構(gòu):
{
"name": "Rose Kolodny",
"phoneNumbers": [
{
"type": "home",
"number": "954-555-1234"
},
{
"type": "work",
"number": "754-555-5678"
}
]
}
JSONPath表達(dá)式:
phoneNumbers[*].number
該表達(dá)式將會(huì)返回一個(gè)集合列表,如下:
[954-555-1234, 754-555-5678]
請(qǐng)注意,這不是JSON數(shù)組,它只是以逗號(hào)分隔的項(xiàng)列表,其中[]表示列表的開頭和結(jié)尾。
對(duì)匹配列表使用“equals”斷言時(shí),請(qǐng)指定[]中包含的預(yù)期值列表,并用逗號(hào)和一個(gè)空格分隔
[apples, 15, false, ["foo","bar"], {"status":"ok"}]
除非引號(hào)是值的一部分,否則獨(dú)立字符串(如apples)不應(yīng)包含引號(hào)
示例
給定下面一個(gè)JSON:
{ "words": ["apples", "\"oranges\""] }
$ .words [*]
返回所有數(shù)組項(xiàng)的列表,因此預(yù)期值為[apples,“oranges”]
。
注意與$ .words
的區(qū)別,它返回JSON中顯示的數(shù)組本身,因此,在這種情況下,值將是[“apples”,“\”oranges \“”]
作為JSON數(shù)組和對(duì)象的值保留內(nèi)部引號(hào),但是在它們的項(xiàng)之間縮小而沒(méi)有空格:[“foo”,“bar”]
,而不是[ “foo” , “bar” ]
。
因?yàn)槲沂且幻鸍ava工程師,看到這里,既然JSONPath是一個(gè)用Java語(yǔ)言開發(fā)的組件,那自然是要學(xué)習(xí)一下的(非Java語(yǔ)言的同學(xué)可以忽略~~~)
首先在Maven項(xiàng)目中加入JSONPath的引用
<!-- https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
假設(shè)有如下JSON結(jié)構(gòu)
{
"name": "Rose Kolodny",
"phoneNumbers": [
{
"type": "home",
"number": "954-555-1234"
},
{
"type": "work",
"number": "754-555-5678"
}
]
}
我們想獲取得到number的數(shù)組集合,應(yīng)該如何做呢
String json="...";
System.out.println("JSON:"+json);
List<String> numbers= JsonPath.read(json,"$..number");
for (String num:numbers){
System.out.println(num);
}
String name=JsonPath.read(json,"$.name");
System.out.println("name:"+name);
最終控制臺(tái)輸出:
954-555-1234
754-555-5678
Rose Kolodny
上面這種方式很麻煩,因?yàn)槟銢](méi)讀取一個(gè)JSON的字段屬性,都需要將源JSON整體傳入到方法中,在對(duì)于程序性能來(lái)說(shuō)是一種消耗,因?yàn)槎夹枰狫SONPath組件重新解析一次JSON的結(jié)構(gòu)
如果你只想JSONPath只初始化一次就可以了,應(yīng)該使用如下方式:
private static void once(String json){
System.out.println("初始化一次");
//初始化創(chuàng)建Document對(duì)象
Object document= Configuration.defaultConfiguration().jsonProvider().parse(json);
List<String> numbers= JsonPath.read(document,"$..number");
for (String num:numbers){
System.out.println(num);
}
String name=JsonPath.read(document,"$.name");
System.out.println("name:"+name);
}
此外,JSONPath還提供了流式API,方便開發(fā)者使用
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);
在使用JSONPath組件時(shí),了解結(jié)果中的預(yù)期類型非常重要,比如在上個(gè)json中,我們查詢name屬性時(shí),使用JSONPath的表達(dá)式$.name
和$..name
就區(qū)別很大,一個(gè)是返回String類型的字符串,一個(gè)是返回?cái)?shù)組,因此,根據(jù)JSONPath預(yù)判返回結(jié)果類型顯得尤為關(guān)鍵。
//將會(huì)拋出java.lang.ClassCastException異常,因?yàn)檫@是返回一個(gè)String字符串的JSONPath表達(dá)式
List<String> list = JsonPath.parse(json).read("$.store.book[0].author")
//正確執(zhí)行
String author = JsonPath.parse(json).read("$.store.book[0].author")
在評(píng)估預(yù)判JSONPath的路徑時(shí),您需要了解路徑何時(shí)確定的概念。如果路徑包含,則路徑是不確定的
..
:一個(gè)遞歸掃描的表示法?(<expression>)
:一個(gè)JSONPath表達(dá)式[<number>,<number>(,<number>)]
:多個(gè)數(shù)組索引對(duì)于不確定的路徑JSONPath始終返回一個(gè)數(shù)組列表
默認(rèn)情況下,MappingProvider SPI提供了一個(gè)簡(jiǎn)單的對(duì)象映射器。這允許您指定所需的返回類型,MappingProvider將嘗試執(zhí)行映射。在下面的示例中,演示了Long和Date之間的映射
String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
如果將JSONPath配置為使用JacksonMappingProvider
或者GsonMappingProvider
,您甚至可以將JSONPath輸出直接映射到POJO上
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
要獲得完整的泛型類型信息,請(qǐng)使用TypeRef
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);
有三種方法來(lái)創(chuàng)建JSONPath組件的過(guò)濾器規(guī)則
List<Map<String, Object>> books = JsonPath.parse(json)
.read("$.store.book[?(@.price < 10)]");
您可以使用&&
和||
組合多個(gè)條件,[?(@.price < 10 && @.category == 'fiction')]
、[?(@.category == 'reference' || @.price > 10)]
您也可以使用!
表示非條件[?(!(@.price < 10 && @.category == 'fiction'))]
通過(guò)JSONPath提供的API來(lái)篩選
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);
注意占位符?
對(duì)于路徑中的過(guò)濾器。當(dāng)提供多個(gè)過(guò)濾器時(shí),它們將按順序應(yīng)用,其中占位符的數(shù)量必須與提供的過(guò)濾器數(shù)量相匹配。
您也可以使用OR
和AND
對(duì)接過(guò)進(jìn)行篩選組合
Filter fooOrBar = filter(
where("foo").exists(true)).or(where("bar").exists(true)
);
Filter fooAndBar = filter(
where("foo").exists(true)).and(where("bar").exists(true)
);
第三種是實(shí)現(xiàn)你自己的Predicate
接口
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實(shí)現(xiàn)中,JsonPath可以返回Path或Value。值是默認(rèn)值,以及上面的所有示例返回的內(nèi)容。如果您更喜歡我們的查詢所遇到的元素的路徑,則可以使用選項(xiàng)來(lái)實(shí)現(xiàn)。
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']");
創(chuàng)建配置時(shí),有一些選項(xiàng)標(biāo)志可以改變默認(rèn)行為。
此選項(xiàng)使JsonPath為缺少的葉子返回null??紤]以下json
[
{
"name" : "john",
"gender" : "male"
},
{
"name" : "ben"
}
]
Java代碼
Configuration conf = Configuration.defaultConfiguration();
//正確運(yùn)行
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']");
//返回null
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
即使路徑是確定的,此選項(xiàng)也會(huì)將JsonPath配置為返回列表
Configuration conf = Configuration.defaultConfiguration();
//Works fine
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
//PathNotFoundException thrown
List<String> genders1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
此選項(xiàng)可確保不會(huì)從路徑評(píng)估傳播任何異常。它遵循以下簡(jiǎn)單規(guī)則
ALWAYS_RETURN_LIST
,則將返回空列表ALWAYS_RETURN_LIST
不存在,則返回null聯(lián)系客服