DSL Java untuk membaca dokumen JSON.
Jayway JsonPath adalah port Java dari implementasi Stefan Goessner JsonPath.
JsonPath tersedia di Repositori Central Maven. Pengguna Maven menambahkan ini ke POM Anda.
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
Jika Anda memerlukan bantuan, ajukan pertanyaan di Stack Overflow. Tandai pertanyaan 'jsonpath' dan 'java'.
Ekspresi JsonPath selalu mengacu pada struktur JSON dengan cara yang sama seperti ekspresi XPath yang digunakan dalam kombinasi dengan dokumen XML. "Objek anggota root" di JsonPath selalu disebut sebagai $
terlepas dari apakah itu objek atau array.
Ekspresi JsonPath dapat menggunakan notasi titik
$.store.book[0].title
atau tanda kurung – notasi
$['store']['book'][0]['title']
Operator | Keterangan |
---|---|
$ | Elemen root yang akan dikueri. Ini memulai semua ekspresi jalur. |
@ | Node saat ini sedang diproses oleh predikat filter. |
* | karakter pengganti. Tersedia di mana saja nama atau angka diperlukan. |
.. | Pemindaian mendalam. Tersedia di mana saja nama diperlukan. |
.<name> | Anak yang diberi notasi titik |
['<name>' (, '<name>')] | Anak atau anak-anak yang diberi tanda kurung |
[<number> (, <number>)] | Indeks array atau indeks |
[start:end] | Operator irisan array |
[?(<expression>)] | Saring ekspresi. Ekspresi harus bernilai boolean. |
Fungsi dapat dipanggil di ujung jalur - masukan ke suatu fungsi adalah keluaran dari ekspresi jalur. Output fungsi ditentukan oleh fungsi itu sendiri.
Fungsi | Keterangan | Jenis keluaran |
---|---|---|
min() | Memberikan nilai minimum dari serangkaian angka | Dobel |
max() | Memberikan nilai maksimal dari serangkaian angka | Dobel |
avg() | Memberikan nilai rata-rata dari serangkaian angka | Dobel |
stddev() | Memberikan nilai deviasi standar dari serangkaian angka | Dobel |
length() | Menyediakan panjang array | Bilangan bulat |
sum() | Memberikan nilai penjumlahan dari array angka | Dobel |
keys() | Menyediakan kunci properti (Alternatif untuk terminal tilde ~ ) | Set<E> |
concat(X) | Menyediakan versi keluaran jalur yang digabungkan dengan item baru | seperti masukan |
append(X) | tambahkan item ke array keluaran jalur json | seperti masukan |
first() | Menyediakan item pertama dari sebuah array | Tergantung pada arraynya |
last() | Menyediakan item terakhir dari sebuah array | Tergantung pada arraynya |
index(X) | Menyediakan item dari array indeks: X, jika X negatif, ambil dari belakang | Tergantung pada arraynya |
Filter adalah ekspresi logis yang digunakan untuk memfilter array. Filter umumnya adalah [?(@.age > 18)]
dengan @
mewakili item yang sedang diproses. Filter yang lebih kompleks dapat dibuat dengan operator logika &&
dan ||
. Literal string harus diapit oleh tanda kutip tunggal atau ganda ( [?(@.color == 'blue')]
atau [?(@.color == "blue")]
).
Operator | Keterangan |
---|---|
== | kiri sama dengan kanan (perhatikan bahwa 1 tidak sama dengan '1') |
!= | kiri tidak sama dengan kanan |
< | kiri lebih kecil dari kanan |
<= | kiri lebih kecil atau sama dengan kanan |
> | kiri lebih besar dari kanan |
>= | kiri lebih besar atau sama dengan kanan |
=~ | kiri cocok dengan ekspresi reguler [?(@.name =~ /foo.*?/i)] |
in | kiri ada di kanan [?(@.size di ['S', 'M'])] |
nin | kiri tidak ada di kanan |
subsetof | kiri adalah subset dari kanan [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | kiri berpotongan dengan kanan [?(@.sizes anyof ['M', 'L'])] |
noneof | kiri tidak berpotongan dengan kanan [?(@.sizes noneof ['M', 'L'])] |
size | ukuran kiri (array atau string) harus cocok dengan kanan |
empty | kiri (array atau string) harus kosong |
Mengingat 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 | Hasil |
---|---|
$.store.book[*].author | Penulis semua buku |
$..author | Semua penulis |
$.store.* | Segala sesuatu, baik buku maupun sepeda |
$.store..price | Harga segalanya |
$..book[2] | Buku ketiga |
$..book[-2] | Buku kedua dari terakhir |
$..book[0,1] | Dua buku pertama |
$..book[:2] | Semua buku dari indeks 0 (inklusif) hingga indeks 2 (eksklusif) |
$..book[1:2] | Semua buku dari indeks 1 (inklusif) hingga indeks 2 (eksklusif) |
$..book[-2:] | Dua buku terakhir |
$..book[2:] | Semua buku dari indeks 2 (inklusif) hingga terakhir |
$..book[?(@.isbn)] | Semua buku dengan nomor ISBN |
$.store.book[?(@.price < 10)] | Semua buku di toko lebih murah dari 10 |
$..book[?(@.price <= $['expensive'])] | Semua buku di toko yang tidak "mahal" |
$..book[?(@.author =~ /.*REES/i)] | Semua buku cocok dengan regex (abaikan huruf besar/kecil) |
$..* | Beri aku segalanya |
$..book.length() | Jumlah buku |
Cara termudah dan paling mudah untuk menggunakan JsonPath adalah melalui API baca statis.
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
Jika Anda hanya ingin membaca sekali, tidak apa-apa. Jika Anda perlu membaca jalur lain juga, ini bukan cara yang tepat karena dokumen akan diuraikan setiap kali Anda memanggil JsonPath.read(...). Untuk menghindari masalah Anda dapat mengurai json terlebih dahulu.
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 juga menyediakan API yang lancar. Ini juga yang paling fleksibel.
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 );
Saat menggunakan JsonPath di Java, penting untuk mengetahui tipe apa yang Anda harapkan dalam hasil Anda. JsonPath secara otomatis akan mencoba memberikan hasil ke tipe yang diharapkan oleh pemanggil.
//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" );
Saat mengevaluasi suatu jalur, Anda perlu memahami konsep kapan suatu jalur definite
. Suatu jalur indefinite
jika berisi:
..
- operator pemindaian mendalam?(<expression>)
- sebuah ekspresi[<number>, <number> (, <number>)]
- beberapa indeks array Jalur Indefinite
selalu mengembalikan daftar (seperti yang diwakili oleh JsonProvider saat ini).
Secara default, pembuat peta objek sederhana disediakan oleh MappingProvider SPI. Hal ini memungkinkan Anda menentukan tipe pengembalian yang Anda inginkan dan MappingProvider akan mencoba melakukan pemetaan. Dalam contoh di bawah ini, pemetaan antara Long
dan Date
ditunjukkan.
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
Jika Anda mengonfigurasi JsonPath untuk menggunakan JacksonMappingProvider
, GsonMappingProvider
, atau JakartaJsonProvider
Anda bahkan dapat memetakan keluaran JsonPath langsung ke POJO.
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
Untuk mendapatkan informasi tipe generik lengkap, gunakan TypeRef.
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
Ada tiga cara berbeda untuk membuat predikat filter di JsonPath.
Predikat sebaris adalah predikat yang ditentukan di jalur.
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
Anda dapat menggunakan &&
dan ||
untuk menggabungkan beberapa predikat [?(@.price < 10 && @.category == 'fiction')]
, [?(@.category == 'reference' || @.price > 10)]
.
Anda dapat menggunakan !
untuk meniadakan predikat [?(!(@.price < 10 && @.category == 'fiction'))]
.
Predikat dapat dibangun menggunakan Filter API seperti yang ditunjukkan di bawah ini:
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 );
Perhatikan placeholdernya ?
untuk filter di jalur. Jika beberapa filter disediakan, maka filter tersebut akan diterapkan secara berurutan sehingga jumlah placeholder harus sesuai dengan jumlah filter yang disediakan. Anda dapat menentukan beberapa predikat placeholder dalam satu operasi filter [?, ?]
, kedua predikat harus cocok.
Filter juga dapat digabungkan dengan 'OR' dan '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 )
);
Opsi ketiga adalah menerapkan predikat Anda sendiri
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 );
Dalam implementasi Goessner, JsonPath dapat mengembalikan Path
atau Value
. Value
adalah default dan semua contoh di atas mengembalikannya. Jika Anda lebih suka mengetahui jalur elemen yang sesuai dengan kueri kami, hal ini dapat dicapai dengan sebuah opsi.
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']" );
Perpustakaan menawarkan kemungkinan untuk menetapkan nilai.
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
Saat membuat Konfigurasi Anda, ada beberapa tanda opsi yang dapat mengubah perilaku default.
DEFAULT_PATH_LEAF_TO_NULL
Opsi ini membuat JsonPath mengembalikan null untuk daun yang hilang. Perhatikan json berikut
[
{
"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']" );
SELALU_RETURN_LIST
Opsi ini mengonfigurasi JsonPath untuk mengembalikan daftar meskipun jalurnya definite
.
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
Opsi ini memastikan tidak ada pengecualian yang disebarkan dari evaluasi jalur. Ini mengikuti aturan sederhana berikut:
ALWAYS_RETURN_LIST
ada, daftar kosong akan dikembalikanALWAYS_RETURN_LIST
TIDAK ada, null dikembalikan REQUIRE_PROPERTIES
Opsi ini mengonfigurasi JsonPath agar memerlukan properti yang ditentukan di jalur ketika jalur indefinite
dievaluasi.
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 dikirimkan dengan lima JSONProvider berbeda:
Mengubah default konfigurasi seperti yang ditunjukkan hanya boleh dilakukan saat aplikasi Anda sedang diinisialisasi. Perubahan selama runtime sangat tidak disarankan, terutama pada aplikasi multi-thread.
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 );
}
});
Perhatikan bahwa JacksonJsonProvider memerlukan com.fasterxml.jackson.core:jackson-databind:2.4.5
dan GsonJsonProvider memerlukan com.google.code.gson:gson:2.3.1
di classpath Anda.
Baik penyedia Jakarta EE 9 JSON-P (JSR-342) dan JSON-B (JSR-367) mengharapkan setidaknya Java 8 dan memerlukan implementasi JSON API yang kompatibel (seperti Eclipse Glassfish dan Eclipse Yasson) pada classpath runtime aplikasi; implementasi tersebut juga dapat disediakan oleh wadah aplikasi Java EE. Perlu diketahui juga bahwa Apache Johnzon belum kompatibel dengan classpath dengan spesifikasi Jakarta EE 9, dan jika penyedia pemetaan JSON-B dipilih maka penyedia JSON-P juga harus dikonfigurasi dan digunakan.
Salah satu kekhasan spesifikasi Jakarta EE 9 untuk pemrosesan JSON dan penyatuan data (pemetaan) adalah kekekalan array dan objek Json segera setelah diurai atau ditulis sepenuhnya. Untuk mematuhi spesifikasi API, namun memungkinkan JsonPath memodifikasi dokumen Json melalui operasi tambah, set/letakkan, ganti, dan hapus, JakartaJsonProvider
harus diinisialisasi dengan argumen opsional true
:
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(mengaktifkan array dan objek Json yang dapat diubah)JsonProvider jsonProvider = new JakartaJsonProvider()
(default, kepatuhan API JSON-P yang ketat)Semua operasi pencarian dan pembacaan dengan JsonPath didukung apa pun mode inisialisasinya. Mode default juga memerlukan lebih sedikit memori dan lebih berkinerja.
Di JsonPath 2.1.0, Cache SPI baru diperkenalkan. Hal ini memungkinkan konsumen API untuk mengonfigurasi cache jalur dengan cara yang sesuai dengan kebutuhan mereka. Cache harus dikonfigurasi sebelum diakses untuk pertama kalinya atau JsonPathException dilemparkan. JsonPath dikirimkan dengan dua implementasi cache
com.jayway.jsonpath.spi.cache.LRUCache
(default, aman untuk thread)com.jayway.jsonpath.spi.cache.NOOPCache
(tanpa cache)Jika Anda ingin mengimplementasikan cache Anda sendiri, APInya sederhana.
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 );
}
});