JSON ドキュメントを読み取るための Java DSL。
Jayway JsonPath は、Stefan Goessner JsonPath 実装の Java ポートです。
JsonPath は Central Maven リポジトリで入手できます。 Maven ユーザーはこれを POM に追加します。
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
サポートが必要な場合は、Stack Overflow で質問してください。質問に「jsonpath」と「java」のタグを付けます。
JsonPath 式は、XPath 式が XML ドキュメントと組み合わせて使用されるのと同じ方法で、常に JSON 構造を参照します。 JsonPath の「ルート メンバー オブジェクト」は、オブジェクトであるか配列であるかに関係なく、常に$
として参照されます。
JsonPath 式ではドット表記を使用できます
$.store.book[0].title
または括弧表記
$['store']['book'][0]['title']
オペレーター | 説明 |
---|---|
$ | クエリするルート要素。これにより、すべてのパス式が開始されます。 |
@ | フィルター述語によって処理されている現在のノード。 |
* | ワイルドカード。名前または数値が必要な場合はどこでも使用できます。 |
.. | ディープスキャン。名前が必要な場所ならどこでも利用可能です。 |
.<name> | ドット表記の子 |
['<name>' (, '<name>')] | 括弧付きの子または子 |
[<number> (, <number>)] | 配列インデックス (複数可) |
[start:end] | 配列スライス演算子 |
[?(<expression>)] | フィルター式。式はブール値に評価される必要があります。 |
関数はパスの最後で呼び出すことができます。関数への入力はパス式の出力です。関数の出力は関数自体によって決まります。
関数 | 説明 | 出力タイプ |
---|---|---|
min() | 数値配列の最小値を提供します | ダブル |
max() | 数値配列の最大値を提供します | ダブル |
avg() | 数値配列の平均値を提供します | ダブル |
stddev() | 数値配列の標準偏差値を提供します。 | ダブル |
length() | 配列の長さを提供します | 整数 |
sum() | 数値配列の合計値を提供します | ダブル |
keys() | プロパティ キーを提供します (ターミナルのチルダ~ の代替) | Set<E> |
concat(X) | 新しい項目を含むパス出力の連結バージョンを提供します | 入力のような |
append(X) | json パス出力配列に項目を追加します | 入力のような |
first() | 配列の最初の項目を提供します | アレイに依存します |
last() | 配列の最後の項目を提供します | アレイに依存します |
index(X) | インデックスの配列の項目を提供します: X。X が負の場合は、後方から取得します。 | アレイに依存します |
フィルターは、配列をフィルター処理するために使用される論理式です。一般的なフィルターは[?(@.age > 18)]
になります。ここで、 @
処理されている現在の項目を表します。論理演算子&&
および||
を使用すると、より複雑なフィルターを作成できます。 。文字列リテラルは一重引用符または二重引用符 ( [?(@.color == 'blue')]
または[?(@.color == "blue")]
] で囲む必要があります。
オペレーター | 説明 |
---|---|
== | 左は右と等しい (1 は '1' と等しくないことに注意) |
!= | 左は右と等しくない |
< | 左は右より小さい |
<= | 左は右と等しいかそれより小さい |
> | 左は右より大きい |
>= | 左が右以上である |
=~ | left は正規表現 [?(@.name =~ /foo.*?/i)] に一致します |
in | 左は右に存在します [?(@.size in ['S', 'M'])] |
nin | 左は右に存在しない |
subsetof | 左は右のサブセット [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | 左は右と交差しています [?(@.sizes anyof ['M', 'L'])] |
noneof | 左には右との交差がありません [?(@.sizes noneof ['M', 'L'])] |
size | 左側のサイズ (配列または文字列) は右側と一致する必要があります |
empty | left (配列または文字列) は空である必要があります |
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
}
JsonPath | 結果 |
---|---|
$.store.book[*].author | すべての本の著者 |
$..author | すべての著者 |
$.store.* | 本も自転車もすべて |
$.store..price | あらゆるものの価格 |
$..book[2] | 3冊目の本 |
$..book[-2] | 最後から2番目の本 |
$..book[0,1] | 最初の2冊 |
$..book[:2] | インデックス 0 (これを含む) からインデックス 2 (これを除く) までのすべての書籍 |
$..book[1:2] | インデックス 1 (含む) からインデックス 2 (含まない) までのすべての書籍 |
$..book[-2:] | 最後の2冊 |
$..book[2:] | インデックス 2 (両端を含む) から最後までのすべての書籍 |
$..book[?(@.isbn)] | ISBN 番号のあるすべての書籍 |
$.store.book[?(@.price < 10)] | 店内のすべての本が 10 冊より安い |
$..book[?(@.price <= $['expensive'])] | 店内にある「高価」ではないすべての本 |
$..book[?(@.author =~ /.*REES/i)] | 正規表現に一致するすべての書籍 (大文字と小文字は無視) |
$..* | すべてを私に与えてください |
$..book.length() | 冊数 |
JsonPath を使用する最も単純で直接的な方法は、静的読み取り API を使用することです。
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
一度だけ読みたい場合はこれでOKです。他のパスも読み取る必要がある場合、JsonPath.read(...) を呼び出すたびにドキュメントが解析されるため、これは適した方法ではありません。この問題を回避するには、最初に 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" );
JsonPath は流暢な 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 で JsonPath を使用する場合、結果にどのような型が含まれるかを理解しておくことが重要です。 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" );
パスを評価するときは、パスがdefinite
あるときの概念を理解する必要があります。以下が含まれる場合、パスはindefinite
なります。
..
- ディープ スキャン オペレーター?(<expression>)
- 式[<number>, <number> (, <number>)]
- 複数の配列インデックスIndefinite
パスは常にリスト (現在の JsonProvider で表される) を返します。
デフォルトでは、単純なオブジェクト マッパーが MappingProvider SPI によって提供されます。これにより、必要な戻り値の型を指定できるようになり、MappingProvider がマッピングの実行を試みます。以下の例では、 Long
とDate
の間のマッピングが示されています。
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
JacksonMappingProvider
、 GsonMappingProvider
、またはJakartaJsonProvider
を使用するように JsonPath を構成すると、JsonPath 出力を POJO に直接マッピングすることもできます。
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
完全なジェネリック型情報を取得するには、TypeRef を使用します。
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
JsonPath でフィルター述部を作成するには 3 つの異なる方法があります。
インライン述語は、パス内で定義された述語です。
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
&&
と||
使用できます。複数の述語を組み合わせるには[?(@.price < 10 && @.category == 'fiction')]
、 [?(@.category == 'reference' || @.price > 10)]
。
使えます!
述語[?(!(@.price < 10 && @.category == 'fiction'))]
否定します。
以下に示すように、フィルター API を使用して述語を構築できます。
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 );
プレースホルダーに気づきましたか?
パス内のフィルターの場合。複数のフィルターが提供される場合、それらはプレースホルダーの数が提供されたフィルターの数と一致する必要がある順序で適用されます。 1 つのフィルター操作[?, ?]
で複数の述語プレースホルダーを指定できます。両方の述語が一致する必要があります。
フィルターは「OR」や「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 )
);
3 番目のオプションは、独自の述語を実装することです
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 );
Goessner 実装では、 JsonPath はPath
またはValue
を返すことができます。 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']" );
ライブラリには値を設定する機能が用意されています。
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
構成を作成するときに、デフォルトの動作を変更できるオプション フラグがいくつかあります。
DEFAULT_PATH_LEAF_TO_NULL
このオプションにより、JsonPath は葉が欠落している場合に null を返します。次の json を考えてみましょう
[
{
"name" : "john" ,
"gender" : "male"
} ,
{
"name" : "ben"
}
]
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']" );
ALWAYS_RETURN_LIST
このオプションは、パスがdefinite
場合でもリストを返すように JsonPath を構成します。
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']" );
SUPPRESS_EXCEPTIONS
このオプションにより、パス評価から例外が伝播されなくなります。これは次の簡単なルールに従います。
ALWAYS_RETURN_LIST
が存在する場合、空のリストが返されます。ALWAYS_RETURN_LIST
が存在しない場合は、null が返されますREQUIRE_PROPERTIES
このオプションは、 indefinite
パスが評価されるときにパスで定義されたプロパティを要求するように JsonPath を構成します。
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
List < String > genders = JsonPath . using ( conf ). parse ( json ). read ( "$[*]['gender']" );
Configuration conf2 = conf . addOptions ( Option . REQUIRE_PROPERTIES );
//PathNotFoundException thrown
List < String > genders = JsonPath . using ( conf2 ). parse ( json ). read ( "$[*]['gender']" );
JsonPath には、次の 5 つの異なる JsonProvider が付属しています。
示されているように構成のデフォルトを変更するのは、アプリケーションの初期化中にのみ行う必要があります。実行時の変更は、特にマルチスレッド アプリケーションでは行わないことを強くお勧めします。
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 にはクラスパス上のcom.fasterxml.jackson.core:jackson-databind:2.4.5
が必要であり、GsonJsonProvider にはcom.google.code.gson:gson:2.3.1
必要であることに注意してください。
Jakarta EE 9 JSON-P (JSR-342) プロバイダーと JSON-B (JSR-367) プロバイダーは両方とも、少なくとも Java 8 を想定しており、アプリケーション ランタイム クラスパス上に互換性のある JSON API 実装 (Eclipse Glassfish や Eclipse Yasson など) を必要とします。このような実装は、Java EE アプリケーション コンテナによって提供される場合もあります。また、Apache Johnzon はまだ Jakarta EE 9 仕様とクラスパス互換性がないことにも注意してください。JSON-B マッピング プロバイダーが選択されている場合は、JSON-P プロバイダーも構成して使用する必要があります。
JSON 処理とデータバインディング (マッピング) に関する Jakarta EE 9 仕様の特徴の 1 つは、Json 配列とオブジェクトが完全に解析または書き込まれた直後から不変であることです。 API 仕様を尊重しながら、JsonPath が追加、設定/配置、置換、削除操作を通じて Json ドキュメントを変更できるようにするには、オプションのtrue
引数を使用してJakartaJsonProvider
初期化する必要があります。
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(変更可能な Json 配列とオブジェクトを有効にする)JsonProvider jsonProvider = new JakartaJsonProvider()
(デフォルト、厳密な JSON-P API 準拠)JsonPath を使用したすべての検索および読み取り操作は、初期化モードに関係なくサポートされます。デフォルト モードでは必要なメモリも少なくなり、パフォーマンスが向上します。
JsonPath 2.1.0 では、新しいキャッシュ SPI が導入されました。これにより、API コンシューマーはニーズに合った方法でパス キャッシュを構成できるようになります。キャッシュは、初めてアクセスする前に構成する必要があります。そうしないと、JsonPathException がスローされます。 JsonPath には 2 つのキャッシュ実装が付属しています
com.jayway.jsonpath.spi.cache.LRUCache
(デフォルト、スレッドセーフ)com.jayway.jsonpath.spi.cache.NOOPCache
(キャッシュなし)独自のキャッシュを実装したい場合、API は簡単です。
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 );
}
});