強大!JSON解析神器,再復雜也不怕了
環境:SpringBoot3.4.0
1. 簡介
你是否遇到過需要從一個復雜的JSON結構中提取特定信息的場景?
當你面對一個嵌套層級很多、結構復雜的JSON對象時,你是否覺得逐一創建對應的Java實體類(或者是Map)來接收這些數據既繁瑣又低效?
在進行單元測試或模擬請求時,你是否需要構建或修改JSON數據?
在測試過程中,我們可能需要構建特定的JSON請求體或修改響應數據。有沒有一種工具可以幫助我們輕松地完成這些操作?
答案是肯定的!有一種名為JSONPath的工具,它提供了一種類似于XPath的表達式語言,用于在JSON文檔中查詢和操作數據。JSONPath不僅能夠高效地定位和提取JSON中的特定信息,還支持數據過濾、轉換和更新等操作。
2. 實戰案例
2.1 準備環境
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
如果你是基于Spring Boot環境,那么你不需要指定版本,Spring Boot已經幫我們管理了該組件的版本。截至撰寫該篇文章時,JSONPath的最新版本是2.9.0。
說明:JsonPath表達式總是以與XPath表達式在XML文檔中結合使用相同的方式來引用JSON結構。在JsonPath中,“根成員對象”無論是對象還是數組,總是被引用為$。
2.2 語法介紹
JsonPath 表達式可以使用點符號"."
$.store.book[0].title
也可以使用括號表示法
$['store']['book'][0]['title']
操作符
操作 | 說明 |
$ | 查詢的根元素,所有路徑表達式的起點 |
@ | 由過濾器謂詞處理的當前節點 |
* | 通配符,在需要名稱或數字的位置可用 |
.. | 深度掃描,在需要名稱的位置可用 |
.<name> | 點表示法的子節點 |
['<name>' (, '<name>')] | 方括號表示法的子節點或子節點集合 |
[<number> (, <number>)] | 數組索引或索引集合 |
[start:end] | 數組切片操作符 |
[?(<expression>)] | 過濾器表達式,表達式必須計算為布爾值 |
函數
函數可以在路徑的末尾被調用——函數的輸入是路徑表達式的輸出。函數的輸出由函數本身決定。支持如下的函數:
過濾操作
過濾器是用于篩選數組的邏輯表達式。一個典型的過濾器可能是[?(@.age > 18)],其中@代表當前正在處理的項??梢允褂眠壿嬤\算符&&和||創建更復雜的過濾器。字符串字面量必須用單引號或雙引號括起來(例如[?(@.color == 'blue')]或[?(@.color == "blue")])。支持如下過濾:
操作符 | 說明 |
== | 左值等于右值(注意1不等于'1') |
!= | 左值不等于右值 |
< | 左值小于右值 |
<= | 左值小于或等于右值 |
> | 左值大于右值 |
>= | 左值大于或等于右值 |
=~ | 左值匹配正則表達式 [?(@.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 | 左值(數組或字符串)應為空 |
接下來,我們介紹具體的使用示例。
2.3 使用示例
準備json數據
{
"store": {
"book": [
{
"category": "Java",
"author": "張三",
"title": "Java從入門到放棄",
"price": 88.6
},
{
"category": "Spring",
"author": "Pack",
"title": "Spring從入門到精通",
"price": 99.8
},
{
"category": "Java",
"author": "李四",
"title": "多線程并發編程",
"isbn": "1-9527-6688-9",
"price": 66.9
},
{
"category": "Spring",
"author": "Pack",
"title": "Spring Boot3實戰案例100講",
"isbn": "6-9527-7799-2",
"price": 70
}
],
"bicycle": {
"color": "red",
"price": 666
}
},
"expensive": 10
}
層級格式如下:
接下來,我們將在代碼中通過jsonpath來操作上面的json數據。
讀取json內容
String json = new ClassPathResource("data.json")
.getContentAsString(StandardCharsets.UTF_8) ;
下面都將基于上面讀取到的json數據進行操作。
獲取所有圖書的作者
List<String> list = JsonPath.parse(json).read("$.store.book[*].author");
System.err.println("Authors: " + list) ;
// 輸出結果
Authors: ["張三","Pack","李四","Pack"]
獲取所有作者
List<String> list = JsonPath.parse(json).read("$..author") ;
System.err.println("Authors: " + list) ;
// 輸出結果
Authors: ["張三","Pack","李四","Pack"]
獲取store節點下的所有數據
Object result = JsonPath.parse(json).read("$.store.*") ;
System.err.println(result) ;
輸出結果如下:
類型是:net.minidev.json.JSONArray
獲取所有價格price
/**獲取store節點下的所有price*/
List<Double> prices = JsonPath.parse(json).read("$.store..price") ;
System.err.println(prices) ;
// 輸出結果
[88.6,99.8,66.9,70,666]
獲取指定索引的書
/**獲取第三本書*/
Object result = JsonPath.parse(json).read("$..book[2]") ;
System.err.println(result) ;
/**輸出結果; 類型:net.minidev.json.JSONArray*/
[{"category":"Java","author":"李四","title":"多線程并發編程","isbn":"1-9527-6688-9","price":66.9}]
result = JsonPath.parse(json).read("$..book[0,1]") ;
System.err.println(result) ;
// 輸出結果
[
{"category":"Java","author":"張三","title":"Java從入門到放棄","price":88.6},
{"category":"Spring","author":"Pack","title":"Spring從入門到精通","price":99.8}
]
所有帶有ISBN編號的書
/**所有帶有ISBN編號的書*/
Object result = JsonPath.parse(json).read("$..book[?(@.isbn)]") ;
System.err.println(result) ;
// 輸出結果
[
{"category":"Java","author":"李四","title":"多線程并發編程","isbn":"1-9527-6688-9","price":66.9},
{"category":"Spring","author":"Pack","title":"Spring Boot3實戰案例100講","isbn":"6-9527-7799-2","price":70}
]
獲取所有價格小于70元的圖書
/**獲取所有價格小于70元的圖書*/
Object result = JsonPath.parse(json).read("$.store.book[?(@.price < 70)]") ;
System.err.println(result) ;
// 輸出結果
[
{"category":"Java","author":"李四","title":"多線程并發編程","isbn":"1-9527-6688-9","price":66.9}
]
獲取圖書的個數
// 獲取圖書的個數
Integer count = JsonPath.parse(json).read("$..book.length()") ;
System.err.println(count) ;
// 輸出結果
4
自定義過濾條件
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
// 自定義filter
Filter cheapFictionFilter = filter(
where("category").is("Java").and("price").lte(70D)
);
List<Map<String, Object>> books = JsonPath.parse(json).read("$.store.book[?]", cheapFictionFilter);
System.err.println(books) ;
// 輸出結果
[
{"category":"Java","author":"李四","title":"多線程并發編程","isbn":"1-9527-6688-9","price":66.9}
]
修改數據
// 修改數據
String newJson = JsonPath.parse(json)
.set("$['store']['book'][0]['author']", "pack_xg")
.jsonString();
System.out.println(newJson) ;
輸出結果:
圖片