ARTS Tips:Java中读取复杂Json串的利器JsonPath

微信扫一扫,分享到朋友圈

ARTS Tips:Java中读取复杂Json串的利器JsonPath

接着上次的Json处理来讨论,层次较深的Json结构在Java代码中访问起来非常不便,层层定义JavaBean不说,写起代码起来要多敲不少按键,同时访问灵活度也不好。

其实有个叫 json-path/JsonPath
的开源java库专门用于解决这种问题,它的主要设计思路是模拟xpath来访问json字符串,拥有强大的访问逻辑和方法,可以用于快速处理json串,尤其是在读取的时候。细看这个库后,可以看到它其实是基于JSONPath这个规范。

JSONPath – XPath for JSON
不是一个正式的规范,是一名叫 Stefan Goessner
提出的访问Json串的规范,主要的思路就是借鉴XPATH来提供一套应用于JSON格式的方法,作者同时给出了 js
php
的实现,从使用方法来看,这是一个很实用的规范。JSONPath和Xpath对应的基本访问方式定义对比如下

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

用个官方的例子来解释一下

{ "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
}
}
}
复制代码

下面是些常见的用法

XPath JSONPath Result
/store/book/author $.store.book[*].author the authors of all books in the store
//author $..author all authors
/store/* $.store.* all things in store, which are some books and a red bicycle.
/store//price $.store..price the price of everything in the store.
//book[3] $..book[2] the third book
//book[last()] $..book[(@.length-1)]
$..book[-1:]
the last book in order.
//book[position()<3] $..book[0,1]
$..book[:2]
the first two books
//book[isbn] $..book[?(@.isbn)] filter all books with isbn number
//book[price<10] $..book[?(@.price<10)] filter all books cheapier than 10
//* $..* all Elements in XML document. All members of JSON structure.

除了可以快速访问以外,还有一个过滤的功能也相当实用,表格中倒数第二个、倒数第三个就是不错的例子。

了解完规范,我们回到Java版的 json-path/JsonPath: Java JsonPath implementation
,这里赞一下 kallestenflo
,他这个库的设计和实现的非常好,远超我的预期,比官方的js实现也要完整的多,在java中方法非常较简单,首先添加依赖

<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
复制代码

具体用法如下

String json = "...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
List<String> authors = JsonPath.read(document, "$.store.book[*].author");
String author0 = JsonPath.read(document, "$.store.book[0].author");
String author1 = JsonPath.read(document, "$.store.book[1].author");
// If you configure JsonPath to use JacksonMappingProvider or GsonMappingProvider you can even map your JsonPath output directly into POJO's.
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
// To obtain full generics type information, use TypeRef.
TypeRef<List<String>> typeRef = new TypeRef<List<String>>() {};
List<String> titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);
复制代码

fluent风格的API如下

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);
复制代码

Java版本还提供一些实用功能:

String newJson = JsonPath.parse(json).set("$['store']['book'][0]['author']", "Paul").jsonString();
Inline Predicates
Filter Predicates

这个库的还有一个比较有用的地方是在爬虫中处理得到的数据,在爬取数据的过程中,服务端的API每个都对应实现一套JavaBean,必要性也不是很强,这个时候就很适合发挥作用。

再补充一下,在Java里生成Json串的话,最基本的方法是用JavaBean序列化,不过还有些其它办法,这里提到了一种快速的方法,如果格式比较固定,其实用一个模板语言来生成Json串也比较实用,主要的优势是比较直观。另外一个思路就是先生成Map对象,然后把这个Map转化为Json字符串,这个有一堆的库可以实现。

最后总结一下,在java中想要轻松愉快的和Json打交道,有不少方法可以尝试,这些方法是借鉴自其它语言或领域,再转化为java专用的工具之后,也足够好用,关键在于使用者的工具箱里有没有这些工具。

Reference

微信扫一扫,分享到朋友圈

ARTS Tips:Java中读取复杂Json串的利器JsonPath

技术总监手把手教我如何消除项目中丑陋的Try{}Catch{},获益匪浅

上一篇

“摆摊神车”让这只仙股暴涨150%,但五菱们没法躺赢

下一篇

你也可能喜欢

ARTS Tips:Java中读取复杂Json串的利器JsonPath

长按储存图像,分享给朋友