在我們使用JSON input組件的時(shí)候,設(shè)置字段映射時(shí),由于Kettle使用的是JSONPath組件來(lái)進(jìn)行解析的,因此我們就需要了解他的相關(guān)語(yǔ)法
JSONPath是一個(gè)用于讀取JSON的Java DSL操作庫(kù)
GitHub:https://github.com/json-path/JsonPath
在線調(diào)試:http://jsonpath./
表示法
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ù)必在屬性名稱(chēng)周?chē)由蠁我?hào)。 如果屬性名稱(chēng)包含空格等特殊字符,或者以A…Za…z_以外的字符開(kāi)頭,請(qǐng)使用此表示法 | [n] | 從數(shù)組中選擇第n個(gè)元素。索引從0開(kāi)始。 | [index1,index2,…] | 選擇具有指定索引的數(shù)組元素。返回一個(gè)集合列表。 | ..property | 遞歸查找:遞歸搜索指定的屬性名稱(chēng),并返回具有此屬性名稱(chēng)的所有值的數(shù)組。即使只找到一個(gè)屬性,也始終返回一個(gè)列表。 | * | 通配符選擇對(duì)象或數(shù)組中的所有元素,無(wú)論其名稱(chēng)或索引如何。例如,address.*。*表示地址對(duì)象的所有屬性,book [*]表示書(shū)籍?dāng)?shù)組的所有項(xiàng)目 | [start:end] or [start:] | 從起始索引中選擇數(shù)組元素,最多但不包括結(jié)束索引。如果省略end,則從開(kāi)始到數(shù)組結(jié)束選擇所有元素。返回一個(gè)列表。 | [:n] | 選擇數(shù)組的前n個(gè)元素。返回一個(gè)列表。 | [-n:] | 選擇數(shù)組的最后n個(gè)元素。返回一個(gè)列表。 | [?expression] | 過(guò)濾表達(dá)式。選擇對(duì)象或數(shù)組中與指定過(guò)濾器匹配的所有元素。返回一個(gè)列表。 | [(expression)] | 可以使用腳本表達(dá)式代替顯式屬性名稱(chēng)或索引。一個(gè)例子是[(@.length-1)],它選擇數(shù)組中的最后一項(xiàng)。這里,length指的是當(dāng)前數(shù)組的長(zhǎng)度,而不是名為length的JSON字段。 | @ | 在過(guò)濾器表達(dá)式中用于引用正在處理的當(dāng)前節(jié)點(diǎn)。 |
注意:
- JSONPath表達(dá)式(包括屬性名稱(chēng)和值)區(qū)分大小寫(xiě)。
- 與XPath不同,JSONPath沒(méi)有用于從給定節(jié)點(diǎn)訪問(wèn)父節(jié)點(diǎn)或兄弟節(jié)點(diǎn)的操作。
過(guò)濾器
過(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)]
只指定屬性名稱(chēng)的表達(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開(kāi)頭的項(xiàng)(不區(qū)分大小寫(xiě)) | ! | 用于否定,例如[?(!@.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ū)分大小寫(xiě))的屬性對(duì)象集合 | $..book[?(@.category == 'fiction' || @.category == 'reference')] | 返回book對(duì)象屬性中category等于fiction的或者等于reference的對(duì)象集合 | $..* | 根目錄下的JSON結(jié)構(gòu)的所有成員(子對(duì)象,單個(gè)屬性值,數(shù)組項(xiàng))組合成一個(gè)數(shù)組。 |
返回多個(gè)元素的JSONPath表達(dá)式的注意事項(xiàng)
JSONPath查詢(xún)不僅可以返回單個(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)列表,其中[]表示列表的開(kāi)頭和結(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” ]。
Java使用示例
因?yàn)槲沂且幻鸍ava工程師,看到這里,既然JSONPath是一個(gè)用Java語(yǔ)言開(kāi)發(fā)的組件,那自然是要學(xué)習(xí)一下的(非Java語(yǔ)言的同學(xué)可以忽略~~~)
簡(jiǎn)單使用
首先在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,方便開(kāi)發(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);
返回預(yù)期
在使用JSONPath組件時(shí),了解結(jié)果中的預(yù)期類(lèi)型非常重要,比如在上個(gè)json中,我們查詢(xún)name屬性時(shí),使用JSONPath的表達(dá)式$.name和$..name就區(qū)別很大,一個(gè)是返回String類(lèi)型的字符串,一個(gè)是返回?cái)?shù)組,因此,根據(jù)JSONPath預(yù)判返回結(jié)果類(lèi)型顯得尤為關(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ì)象映射器。這允許您指定所需的返回類(lèi)型,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);
要獲得完整的泛型類(lèi)型信息,請(qǐng)使用TypeRef
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);
過(guò)濾
有三種方法來(lái)創(chuàng)建JSONPath組件的過(guò)濾器規(guī)則
內(nèi)聯(lián)
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ò)濾
通過(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)
);
Roll Your Own
第三種是實(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)容。如果您更喜歡我們的查詢(xún)所遇到的元素的路徑,則可以使用選項(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']");
配置信息
選項(xiàng)
創(chuàng)建配置時(shí),有一些選項(xiàng)標(biāo)志可以改變默認(rèn)行為。
DEFAULT_PATH_LEAF_TO_NULL
此選項(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']");
ALWAYS_RETURN_LIST
即使路徑是確定的,此選項(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']");
SUPPRESS_EXCEPTIONS
此選項(xiàng)可確保不會(huì)從路徑評(píng)估傳播任何異常。它遵循以下簡(jiǎn)單規(guī)則
- 如果存在選項(xiàng)
ALWAYS_RETURN_LIST,則將返回空列表 - 如果選項(xiàng)
ALWAYS_RETURN_LIST不存在,則返回null
|