いまいちだった Json-Path を再興

昔、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 を使用してしまう。
というのであまり見かけないのかな。。