JsonPath:針對json的強大的規則解析與引數查詢工具

2022-07-14 18:02:15

專案特點

GitHub專案地址:https://github.com/json-path/JsonPath
主要功能:

  1. 將Json字串轉為Java Map物件(這個不算什麼,FastJson之類的工具都可以)
  2. 通過強大的規則表示式定位欄位,返回欄位值或值集合(很厲害)

支援的規則表示式以及範例(選自專案readme):

JsonPath (點選測試) 結果
$.store.book[*].author The authors of all books
$..author All authors
$.store.* All things, both books and bicycles
$.store..price The price of everything
$..book[2] The third book
$..book[-2] The second to last book
$..book[0,1] The first two books
$..book[:2] All books from index 0 (inclusive) until index 2 (exclusive)
$..book[1:2] All books from index 1 (inclusive) until index 2 (exclusive)
$..book[-2:] Last two books
$..book[2:] Book number two from tail
$..book[?(@.isbn)] All books with an ISBN number
$.store.book[?(@.price < 10)] All books in store cheaper than 10
$..book[?(@.price <= $['expensive'])] All books in store that are not "expensive"
$..book[?(@.author =~ /.*REES/i)] All books matching regex (ignore case)
$..* Give me every thing
$..book.length() The number of books

實戰測試

就用專案readme檔案提供的例子進行測試:

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
}

POM中引入依賴:

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.7.0</version>
</dependency>

寫一個單元測試類:

@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
public class JsonPathTest {
    private final String jsonStr = "...";
    private final Object context = Configuration.defaultConfiguration().jsonProvider().parse(jsonStr);

    @Test
    public void test(){}
}

其中:

  1. 第二個註解可以做到啟動SpringBoot測試類而無需啟動SpringApplication,並且也可以做到元件注入等功能。
  2. jsonStr用來指代之前所述的json字串,這裡簡略寫。
  3. context實際上是由json字串通過預設設定生成的一個Map<String, Object>。

先看看context的型別:

System.out.println("context的型別:" + context.getClass().getSimpleName() + "\n");
------------------------------------------------------------------
output: context的型別:LinkedHashMap

用法1:通過key定位一個value:

jsonpath = "$.store.book[1].price";
result = JsonPath.read(testMap, jsonpath);
System.out.println("contents on " + jsonpath + ": " + result + "\n");
------------------------------------------------------------------
output: contents on $.store.book[1].price: 12.99

用法2:通過key定位一組value:

jsonpath = "$.store.book[*].price";
result = JsonPath.read(context, jsonpath);
System.out.println("contents on " + jsonpath + ": " + result + "\n");
------------------------------------------------------------------
output: contents on $.store.book[*].price: [8.95,12.99,8.99,22.99]

這裡如果去檢視result的type,會發現它是JSONArray型別,是List<Object>介面的實現,當List用就可以了。

用法3:向下遞迴查詢:

jsonpath = "$..price";
result = JsonPath.read(context, jsonpath);
System.out.println("contents on " + jsonpath + ": " + result);
------------------------------------------------------------------
output: contents on $..price: [8.95,12.99,8.99,22.99,19.95]

在測試的時候想起一個問題,設想這樣的場景,如果我們已經有了這樣一個Map,只需要JsonPath提供的規則表示式解析與查詢能力,那麼想要使用JsonPath是不是需要Map->JsonString->context->規則解析->查詢結果?答案是不需要的。
我們已經知道這個context本就是一個Map型別,那麼是不是可以省去中間轉為json string的步驟呢?我們測試看看:

//1. 先將json解析為一個Map
Map<String, Object> testMap = (Map) JSONObject.parseObject(JSON_STRING);
jsonpath = "$..price";
//2. 再將Map傳入read方法,看是否能查詢到結果:
result = JsonPath.read(testMap, jsonpath);
System.out.println("contents on " + jsonpath + ": " + result);
------------------------------------------------------------------
output: contents on $..price: [8.95,12.99,8.99,22.99,19.95]

可以看出,JsonPath的搜尋規則解析能力是支援對普通Map進行查詢的。