昔、JSON が世の中に浸透し始めたころ、かつてXMLを読み込むの XPATH なる読出し
方法があった。JSON を読み込むために
XPATH のようなPath記述ルールで読むものが登場し Google Code Archive に入ってた。
結構、便利な筈で git-hub で公開されており Maven Central リポジトリにも存在する。
https://github.com/json-path/JsonPath
以下のWebサイトで Json-Path の構文も検証・確認できる。
https://jsonpath.herokuapp.com/
Json-Path の構文の基本的な構文のルールは以下、、
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. |
Json-Path で配布しているサンプル、以下JSONで、、
{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Wees", "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 }
price のリスト抽出
try(InputStream in = new FileInputStream("sample.json")){ List<Double> list = JsonPath.read(in, "$.store.book[*].price"); list.stream().forEach(System.out::println); }catch(IOException ex){ ex.printStackTrace(); }
フィルタする例
Filter filter = Filter.filter(Criteria.where("author").regex(Pattern.compile(".*es$"))); List<Double> list = JsonPath.read(in, "$.store.book[?].price", filter);
最大、最小を求めるのは、$.store.book[*].price.min() という解釈かと期待してしまうが、
そうではない!
一度、配列のJSON に落としこんでから改めて parse しないとダメなのであり、
ここら辺が、どうも世の中でメジャーになってない原因ではなかろうか?
try(InputStream in = new FileInputStream("sample.json")){ List<Double> list = JsonPath.read(in, "$.store.book[*].price"); String pricelist = "{'price':" + list + "}"; Double min = JsonPath.read(pricelist, "$.price.min()"); Double max = JsonPath.read(pricelist, "$.price.max()"); Double avg = JsonPath.read(pricelist, "$.price.avg()"); Double stddev = JsonPath.read(pricelist, "$.price.stddev()"); Integer length = JsonPath.read(pricelist, "$.price.length()"); System.out.println("min = " + min ); System.out.println("max = " + max ); System.out.println("avg = " + avg ); System.out.println("stddev = " + stddev ); System.out.println("length = " + length ); }catch(IOException ex){ ex.printStackTrace(); }
通常、数値は、Double 型としてしかパースできない。上の length は配列の個数だから
Integer なのである。
・フィルタが書けること。
・簡単な Min , Max , Avg , を求めることができること。
と魅力的なところもあるはずなのだが、
・PATH 構文がすこし判りにくい?
・同時に複数項目を読み込み複雑なことをするなら、Jackson や Gson を使用してしまう。
というのであまり見かけないのかな。。