Jayway JsonPath介绍
XML的一个经常强调的优点是可以使用大量工具来分析,转换和有选择地从XML文档中提取数据,XPath是这些功能强大的工具之一。jsonPath类似Xpath,可以在json数据结构中进行分析。
对比XPath和JsonPath表达式如下:
XPath | JSONPath | Description |
/ | $ | the root object/element |
. | @ | the current object/element |
/ | . or [] | child operator |
.. | n/a | parent operator |
// | .. | recursive descent. JSONPath borrows this syntax from E4X. |
wildcard. All objects/elements regardless their names. | ||
@ | n/a | attribute access. JSON structures don’t have attributes. |
[] | [] | subscript operator. XPath uses it to iterate over element collections and for predicates. In Javascript and JSON it is the native array operator. |
| | [,] | Union operator in XPath results in a combination of node sets. JSONPath allows alternate names or array indices as a set. |
n/a | [start:end:step] | array slice operator borrowed from ES4. |
[] | ?() | applies a filter (script) expression. |
n/a | () | script expression, using the underlying script engine. |
() | n/a | grouping in Xpath |
Jayway JsonPath是Stefan Goessner JsonPath 的java版本实现,
- github:https://github.com/json-path/JsonPath
- 在线工具:http://jsonpath.herokuapp.com/
Jayway JsonPath功能介绍
1、JsonPath 表达式可以用 . 或者 [] 表示:
$.store.book[0].title
或者
$['store']['book'][0]['title']
2、操作符:
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. |
3、函数:
Function | Description | Output |
---|---|---|
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 |
4、过滤器:
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 |
5、示例:
{
"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
}
JsonPath表达式:
JsonPath (click link to try) | Result |
---|---|
$.store.book[].author | The authors of all books |
$..author | All authors |
<a href=”http://jsonpath.herokuapp.com/?path=$.store.“ rel=”nofollow”>$.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” |
<a href=”http://jsonpath.herokuapp.com/?path=$..book%5B?%28@.author%20=~%20/.REES/i%29%5D” rel=”nofollow”>$..book[?(@.author =~ /.REES/i)] | All books matching regex (ignore case) |
<a href=”http://jsonpath.herokuapp.com/?path=$..“ rel=”nofollow”>$..* | Give me every thing |
$..book.length() | The number of books |
Jayway JsonPath使用
1、执行jsonPath表达式:
String json = "...";
List<String> authors = JsonPath.read(json, "$.store.book[*].author");
如果需要多次执行,避免每次读重新解析json,可以通过如下方式:
String json = "...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
String author0 = JsonPath.read(document, "$.store.book[0].author");
String author1 = JsonPath.read(document, "$.store.book[1].author");
或者:(推荐这种写法)
String json = "...";
ReadContext ctx = JsonPath.parse(json);
List<String> authorsOfBooksWithISBN = ctx.read("$.store.book[?(@.isbn)].author");
2、返回值:
JsonPath将自动尝试将结果强制转换为调用者期望的类型,如果出错将报出异常:
//Will throw an java.lang.ClassCastException
List<String> list = JsonPath.parse(json).read("$.store.book[0].author")
//Works fine
String author = JsonPath.parse(json).read("$.store.book[0].author")
1)当jsonPath表示为下列不确定的,则返回list:
- .. :全局匹配
- ?(
) : 过滤表达式 - [
, (, )] :多索引
2)当表达式明确时,可以通过设置MappingProvider返回指定类型:
默认情况下,MappingProvider SPI提供了一个简单的对象映射器,当指定所需的返回类型时,MappingProvider将尝试执行映射。例如下面示例演示了Long和Date之间的映射。
String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
如果将JsonPath配置为使用JacksonMappingProvider或GsonMappingProvider,您甚至可以将JsonPath输出直接映射到POJO。
Configuration conf = Configuration.builder()
.jsonProvider(new GsonJsonProvider())
.build();
Book book = JsonPath.using(conf).parse(json).read("$.store.book[0]", Book.class);
3)使用TypeRef:
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);
3、配置:
[
{
"name" : "john",
"gender" : "male"
},
{
"name" : "ben"
}
]
1)DEFAULT_PATH_LEAF_TO_NULL:
默认JsonPath表达式在对应的json中找不到数据时会报空指针异常,配置该选项后,找不到的会返回null。
Configuration conf = Configuration.defaultConfiguration();
//Works fine
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']");
//Works fine (null is returned)
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
2)ALWAYS_RETURN_LIST:
配置后JsonPath始终返回list,即使表达式是一个明确的。
Configuration conf = Configuration.defaultConfiguration();
//ClassCastException thrown
List<String> genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
Configuration conf2 = conf.addOptions(Option.ALWAYS_RETURN_LIST);
//Works fine
List<String> genders0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
3)SUPPRESS_EXCEPTIONS
默认Jayway JsonPath在执行jsonPath时会报错异常,配置后异常将被抑制。
- 如果同时指定ALWAYS_RETURN_LIST:返回空list;
- 没有制定ALWAYS_RETURN_LIST is NOT:返回null;
4)REQUIRE_PROPERTIES
禁止通配符,比如 $[*].b,会抛出 PathNotFoundException 异常。
5)AS_PATH_LIST
JsonPath可以返回Path或Value。值是默认值,也是上面所有示例返回的值。可以通过该选项让其返回路径。
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']");
4、保存/修改值:
String newJson = JsonPath.parse(json).set("$['store']['book'][0]['author']", "Paul").jsonString();
5、谓词(Predicates):
主要是filter predicate。有很多种方法实现,例如:
1)Inline Predicates
List<Map<String, Object>> books = JsonPath.parse(json)
.read("$.store.book[?(@.price < 10)]");
/**
*You can use && and || to combine multiple predicates [?(@.price < 10 && @.category == *'fiction')] , [?(@.category == 'reference' || @.price > 10)].
*
*You can use ! to negate a predicate [?(!(@.price < 10 && @.category == 'fiction'))].
*/
2)filter predicates:
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);
//Filters can also be combined with 'OR' and 'AND'
Filter fooOrBar = filter(
where("foo").exists(true)).or(where("bar").exists(true)
);
Filter fooAndBar = filter(
where("foo").exists(true)).and(where("bar").exists(true)
);
注:JsonPath中的占位符?和路径中的过滤器要相互匹配。如果提供了多个过滤器,则会按占位符数量必须与提供的过滤器数量匹配的顺序应用它们。您可以在一个过滤器操作[?,?]中指定多个谓词占位符,两个谓词必须匹配。
3)Roll your own
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);
6、JsonProvider SPI:
Jayway JsonPath提供了一下json provider:
- JsonSmartJsonProvider (default)
- JacksonJsonProvider
- JacksonJsonNodeJsonProvider
- GsonJsonProvider
JsonOrgJsonProvider
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);
}
});
注:JacksonJsonProvider requires com.fasterxml.jackson.core2.4.5 and the GsonJsonProvider requires com.google.code.gson
2.3.1 on your classpath.
我们在解析json时,通常也会判断值得类型是object还是array,可以使用如下方法:
Configuration conf = Configuration.builder().options(Option.AS_PATH_LIST).build();
ReadContext ctx = JsonPath.using(conf).parse(json1_schema);
List<String> requiredPathList = ctx.read("$..required");
for (String path : requiredPathList) {
//TODO 判断required的正确性:如果requiredValueList中元素不是array则continue
String replace = path.replace("$['", "").replace("']['", ",").replace("']", "");
Object object = requiredValueList.get(requiredIndex);
requiredIndex++;
if(object instanceof net.minidev.json.JSONArray) {
//System.out.println(" "+replace+","+object.toString());
pathRequiredMap.put(replace, JSONArray.parseArray(object.toString()));
}
}
7、Cache SPI:
Jayway JsonPath提供了一下cache:
- com.jayway.jsonpath.spi.cache.LRUCache (default, thread safe)
com.jayway.jsonpath.spi.cache.NOOPCache (no 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);
}
});
参考:https://zju-cy.github.io/2019/07/07/JsonPath
还没有评论,来说两句吧...