Java中json處理工具JsonPath的使用教程
最近在思考構(gòu)建一個(gè)服務(wù)編排(Service Orchestration)系統(tǒng),考慮這個(gè)系統(tǒng)至少需要具備以下特征:
使用統(tǒng)一的方法定義服務(wù)功能單元
使用一種通用的方式將一個(gè)或多個(gè)服務(wù)的輸出映射到下游服務(wù)的輸入,映射時(shí)支持基礎(chǔ)的數(shù)據(jù)轉(zhuǎn)換與處理
支持以搭積木的方式將低層服務(wù)功能單元組織成更高層抽象的服務(wù)功能,直至一個(gè)完整的服務(wù)
用戶(hù)編排服務(wù)時(shí),具備較大的靈活性定制業(yè)務(wù)
1 JsonPath是什么
json本質(zhì)上是一個(gè)樹(shù)形數(shù)據(jù)結(jié)構(gòu),同樣作為典型樹(shù)形數(shù)據(jù)結(jié)構(gòu)的xml文檔有XPath(XML Path Language)這種廣泛使用的定位和選擇節(jié)點(diǎn)的表達(dá)式語(yǔ)言,java對(duì)象也有OGNL(Object-Graph Navigation Language)這種對(duì)象屬性定位和導(dǎo)航表達(dá)式語(yǔ)言,JsonPath類(lèi)似于XPath,是一種json數(shù)據(jù)結(jié)構(gòu)節(jié)點(diǎn)定位和導(dǎo)航表達(dá)式語(yǔ)言。
2 JsonPath的基本使用
2.1 JsonPath依賴(lài)
要使用JsonPath,需要在pom.xml文件中添加相應(yīng)的依賴(lài):
2.2 json測(cè)試數(shù)據(jù)
為了便于說(shuō)明,本文用到的json測(cè)試數(shù)據(jù),如果沒(méi)有特別說(shuō)明,使用以下的json數(shù)據(jù):
{
"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
}2.3 讀取json內(nèi)容
需要獲取指定json指定節(jié)點(diǎn)的數(shù)據(jù),提供json字符串和JsonPath表達(dá)式即可:
// 讀取json字符串的expensive葉子節(jié)點(diǎn)的值
Object expensive = JsonPath.read(jsonStr, "$.expensive");
log.info("expensive value: {}, class: {}", expensive, expensive.getClass().getCanonicalName());
// 讀取json字符串store節(jié)點(diǎn)下bicycle節(jié)點(diǎn)的值
Object bicycle = JsonPath.read(jsonStr, "$.store.bicycle");
log.info("bicycle value: {}, class: {}", bicycle, bicycle.getClass().getCanonicalName());
// 讀取json字符串store下第一個(gè)book節(jié)點(diǎn)的值
Object book = JsonPath.read(jsonStr, "$.store.book[0]");
log.info("book value: {}, class: {}", book, book.getClass().getCanonicalName());
// 讀取json字符串所有的author后代節(jié)點(diǎn)的值
Object authors = JsonPath.read(jsonStr, "$..author");
log.info("authors value: {}, class: {}", authors, authors.getClass().getCanonicalName());
// 讀取json字符串中所有price值小于10的值
Object books = JsonPath.read(jsonStr, "$.store.book[?(@.price < 10)]");
log.info("books value: {}, class: {}", books, books.getClass().getCanonicalName());輸出如下日志:
expensive value: 10, class: java.lang.Integer
bicycle value: {color=red, price=19.95}, class: java.util.LinkedHashMap
book value: {category=reference, author=Nigel Rees, title=Sayings of the Century, price=8.95}, class: java.util.LinkedHashMap
authors value: ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"], class: net.minidev.json.JSONArray
books value: [{"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}], class: net.minidev.json.JSONArray
2.3.1 基本語(yǔ)法
在JsonPath表達(dá)式語(yǔ)法中,$表示json頂層節(jié)點(diǎn),.表示直接孩子,所以$.expensive表示json字符串的直接孩子節(jié)點(diǎn)expensive,節(jié)點(diǎn)也可以用[]表示。所以$['expensive']與$.expensive等價(jià)。如果[]中是數(shù)字,則表示數(shù)組的某個(gè)元素,所以$.store.book[0]表示第1本書(shū)。數(shù)組支持切片,$.store.book[0:3]表示第1本到第3本書(shū)。..則表示任意的后代。*則表示所有的子節(jié)點(diǎn)。綜合起來(lái)如下表所示:
| Operator | Description |
|---|---|
| $ | The root element to query. This starts all path expressions. |
| @ | The current node being processed by a filter predicate. |
| * | Wildcard. Available anywhere a name or numeric are required. |
| .. | Deep scan. Available anywhere a name is required. |
| .<name> | Dot-notated child |
| ['<name>' (, '<name>')] | Bracket-notated child or children |
| [<number> (, <number>)] | Array index or indexes |
| [start:end] | Array slice operator |
| [?(<expression>)] | Filter expression. Expression must evaluate to a boolean value. |
JsonPath還支持根據(jù)條件過(guò)濾,表達(dá)式[?(expression)]中括號(hào)內(nèi)為條件表達(dá)式。表達(dá)式$.store.book[?(@.price < 10)]中@表示遍歷到的當(dāng)前book,整個(gè)表達(dá)式就表示store中價(jià)格小于10的所有的book。
JsonPath的條件表達(dá)式支持以下操作符:
| Operator | Description |
|---|---|
| == | left is equal to right (note that 1 is not equal to '1') |
| != | left is not equal to right |
| < | left is less than right |
| <= | left is less or equal to right |
| left is greater than right | |
| >= | left is greater than or equal to right |
| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] |
| in | left exists in right [?(@.size in ['S', 'M'])] |
| nin | left does not exists in right |
| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
| anyof | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] |
| noneof | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] |
| size | size of left (array or string) should match right |
| empty | left (array or string) should be empty |
當(dāng)然一些常用的filter函數(shù)也是要支持的:
| Function | Description | Output type |
|---|---|---|
| min() | Provides the min value of an array of numbers | Double |
| max() | Provides the max value of an array of numbers | Double |
| avg() | Provides the average value of an array of numbers | Double |
| stddev() | Provides the standard deviation value of an array of numbers | Double |
| length() | Provides the length of an array | Integer |
| sum() | Provides the sum value of an array of numbers | Double |
| keys() | Provides the property keys (An alternative for terminal tilde ~) | Set<E> |
| concat(X) | Provides a concatinated version of the path output with a new item | like input |
| append(X) | add an item to the json path output array | like input |
| first() | Provides the first item of an array | Depends on the array |
| last() | Provides the last item of an array | Depends on the array |
| index(X) | Provides the item of an array of index: X, if the X is negative, take from backwards | Depends on the array |
| Filter Operators | ||
詳細(xì)的JsonPath表達(dá)式語(yǔ)法和支持的過(guò)濾條件請(qǐng)參考github官方文檔,文檔本身介紹的已經(jīng)很詳細(xì)了。
2.3.2 返回值
從上面的日志結(jié)果來(lái)看,JsonPath的返回結(jié)果的數(shù)據(jù)類(lèi)型依情況而定,大概情況是(具體實(shí)際情況與底層使用的Json處理組件相關(guān)):
- 如果結(jié)果是json葉子節(jié)點(diǎn),則是葉子節(jié)點(diǎn)對(duì)應(yīng)的基本數(shù)據(jù)類(lèi)型
- 如果是數(shù)組,則是
JSONArray - 如果是對(duì)象,這是
LinkedHashMap
2.3.3 指定返回值類(lèi)型
JsonPath也支持指定返回值類(lèi)型,類(lèi)似于json反序列化
Long expensive = JsonPath.parse(jsonStr).read("$.expensive", Long.class);
Bicycle bicycle = JsonPath.parse(jsonStr).read("$.store.bicycle", Bicycle.class);如果json底層處理組件是jackson或gson,還可以支持泛型,假如使用jackson,先添加jackson-databind依賴(lài):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>然后配置使用jackson進(jìn)行json處理和映射即可,如下所示:
Configuration.setDefaults(new Configuration.Defaults() {
// 底層使用jackson
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);
}
});
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> authors = JsonPath.parse(jsonStr).read("$..author", typeRef);2.4 修改json的值
2.4.1 為節(jié)點(diǎn)設(shè)置新值
Object book = JsonPath.read(jsonStr, "$.store.book[0]");
String target = "{\"x\": 128}";
String modified = JsonPath.parse(target).set("$.x", book).jsonString();輸出:
modified: {"x":{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}}
可見(jiàn)目標(biāo)json字符串的節(jié)點(diǎn)x的值替換為了book的值,新的值與原先的值可以是完全不同的類(lèi)型
2.4.2 為對(duì)象節(jié)點(diǎn)添加新的子節(jié)點(diǎn)
下面的例子為target json字符串添加了一個(gè)名字為k的子節(jié)點(diǎn)。注意,這個(gè)方法只能作用于json對(duì)象,不能是葉子節(jié)點(diǎn)或數(shù)組
Object book = JsonPath.read(jsonStr, "$.store.book[0]");
String target = "{\"x\": 128}";
String modified = JsonPath.parse(target).put("$", "k", book).jsonString();2.4.3 對(duì)數(shù)組添加新的元素
String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).put("$.x", 300).jsonString();2.4.4 重命名節(jié)點(diǎn)名字
String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).renameKey("$", "x", "y").jsonString();2.4.5 刪除
String target = "{\"x\": [128]}";
String modified = JsonPath.parse(target).delete("$.x").jsonString();3 JsonPath的高級(jí)特性
3.1 進(jìn)一步配置你的JsonPath
JsonPath提供了一些配置項(xiàng)來(lái)調(diào)整它的功能,這些配置項(xiàng)配置在Option枚舉類(lèi)中,如下所示:
public enum Option {
DEFAULT_PATH_LEAF_TO_NULL,
ALWAYS_RETURN_LIST,
AS_PATH_LIST,
SUPPRESS_EXCEPTIONS,
REQUIRE_PROPERTIES
}功能說(shuō)明:
- DEFAULT_PATH_LEAF_TO_NULL:當(dāng)路徑表達(dá)的節(jié)點(diǎn)不存在時(shí),不拋出異常,而是返回null值
- ALWAYS_RETURN_LIST:返回值總是一個(gè)list,即使路徑找到的是單個(gè)值
- AS_PATH_LIST:返回找到節(jié)點(diǎn)的路徑,而不是節(jié)點(diǎn)的值
- SUPPRESS_EXCEPTIONS:異常時(shí)不拋出異常
使用配置:
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST).build();
List<String> pathList = JsonPath.using(conf).parse(jsonStr).read("$..author");
// 返回的是author節(jié)點(diǎn)的路徑列表
assertThat(pathList).containsExactly(
"$['store']['book'][0]['author']",
"$['store']['book'][1]['author']",
"$['store']['book'][2]['author']",
"$['store']['book'][3]['author']");3.2 修改json底層處理組件
JsonPath支持多個(gè)json組件,默認(rèn)使用的是JsonSmartJsonProvider,其他的還包括:
JacksonJsonProvider
JacksonJsonNodeJsonProvider
GsonJsonProvider
JsonOrgJsonProvider
JakartaJsonProvider
使用下面的方式可以修改默認(rèn)的json provider:
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);
}
});3.3 性能優(yōu)化
如下的方式每次都需要解析json字符串:
Object book = JsonPath.read(jsonStr, "$.store.book[0]");
可以先解析json字符串,然后在查找,這樣只需要解析一次:
DocumentContext context = JsonPath.parse(jsonStr)
Long expensive = context.read("$.expensive", Long.class);
Bicycle bicycle = context.read("$.store.bicycle", Bicycle.class);還可以使用cache,JsonPath已經(jīng)提供了兩個(gè)cache:
com.jayway.jsonpath.spi.cache.LRUCache (default, thread safe) com.jayway.jsonpath.spi.cache.NOOPCache (no cache)
我們可以這樣配置使用cache:
CacheProvider.setCache(new LRUCache(100));
或者配置一個(gè)我們自己實(shí)現(xiàn)的cache:
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);
}
});到此這篇關(guān)于Java中json處理工具JsonPath的使用教程的文章就介紹到這了,更多相關(guān)Java JsonPath內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
@RequestBody注解Ajax post json List集合數(shù)據(jù)請(qǐng)求400/41
這篇文章主要介紹了@RequestBody注解Ajax post json List集合數(shù)據(jù)請(qǐng)求400/415的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置問(wèn)題及解決方法
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置,我們可以通過(guò)自定義配置數(shù)據(jù)庫(kù)配置類(lèi)來(lái)解決這個(gè)問(wèn)題,方式有很多,不同的業(yè)務(wù)采用的方式也不同,下面我簡(jiǎn)單的介紹我們項(xiàng)目的使用的方法2022-06-06
Java+Springboot搭建一個(gè)在線網(wǎng)盤(pán)文件分享系統(tǒng)
本主要介紹了通過(guò)springboot+freemark+jpa+MySQL實(shí)現(xiàn)的在線網(wǎng)盤(pán)文件分享系統(tǒng),其功能跟百度網(wǎng)盤(pán)非常類(lèi)似,可以實(shí)現(xiàn)文件的上傳、移動(dòng)、復(fù)制、下載等,需要的可以參考一下2021-11-11
Java實(shí)戰(zhàn)之課程信息管理系統(tǒng)的實(shí)現(xiàn)
這篇文章主要介紹了如何利用Java實(shí)現(xiàn)課程信息管理系統(tǒng),文中采用到的技術(shù)有:Springboot、SpringMVC、MyBatis、FreeMarker等,感興趣的可以了解一下2022-04-04
Springboot?配置SqlSessionFactory方式
這篇文章主要介紹了Springboot?配置SqlSessionFactory方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
淺談如何優(yōu)雅地停止Spring Boot應(yīng)用
這篇文章主要介紹了淺談如何優(yōu)雅地停止Spring Boot應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
SpringBoot2.6.x 與 Swagger3 兼容問(wèn)題及解決方法
文章介紹了Spring Boot 2.6.x與Swagger 3兼容性問(wèn)題的解決方法,如果項(xiàng)目中未引入spring-boot-starter-actuator,則在yml文件中加入相關(guān)配置,反之,需要添加其他配置,感興趣的朋友一起看看吧2025-03-03

