Skip to content
Kembali ke Blog
Tutorial

Panduan Sintaks JSONPath: Kueri & Filter JSON dengan Contoh

Pelajari sintaks JSONPath lewat contoh siap salin: root, penelusuran rekursif, wildcard, slice, dan ekspresi filter. Uji setiap kueri langsung di browser Anda.

11 menit membaca

Panduan Sintaks JSONPath: Kueri & Filter JSON dengan Contoh

JSONPath adalah bahasa kueri untuk JSON, mirip seperti XPath yang merupakan bahasa kueri untuk XML. Anda menulis sebuah ekspresi path, lalu evaluator mengembalikan setiap nilai yang cocok. Untuk mengambil semua nama penulis dari sebuah dokumen toko buku, Anda cukup menulis $.store.book[*].author, dan daftar penulisnya langsung muncul tanpa perlu kode traversal sama sekali.

Panduan ini membahas setiap selektor dalam sintaks JSONPath disertai contoh siap salin yang bisa Anda jalankan sambil membaca. Satu hal yang perlu diluruskan sejak awal: ada dua dialek. Dialek Goessner 2007 adalah standar klasik secara de facto, sedangkan RFC 9535 adalah standar formal IETF yang terbit pada Februari 2024. Keduanya sepakat pada path umum, tetapi berbeda di kasus-kasus tepi (edge case), jadi panduan ini menandai perbedaannya begitu muncul. Anda bisa mencoba setiap ekspresi di bawah ini pada JSONPath Tester dan beralih antara kedua mesin untuk membandingkannya.

Berikut lembar contekan (cheat sheet) selektor sebagai titik mula. Sisa artikel ini akan menguraikan tiap baris dengan contoh nyata terhadap satu dokumen JSON bersama.

SelektorArtiContoh
$Root dokumen$
@Elemen saat ini (di dalam filter)[?(@.price < 10)]
.name / ['name']Anggota anak (child)$.store.book
..Penelusuran rekursif (recursive descent)$..author
*Semua elemen / anggota$.store.book[*]
[0]Indeks array$.store.book[0]
[start:end:step]Slice array (setengah-terbuka)$.store.book[0:2]
[a,b]Gabungan (union) nama / indeks$.store.book[0,2]
[?()]Ekspresi filter$.store.book[?(@.price < 10)]
length() count() match() search() value()Fungsi RFC 9535 (khusus filter)[?length(@.title) > 15]

Apa Itu JSONPath?

JSONPath adalah bahasa kueri deklaratif untuk memilih node dari sebuah dokumen JSON. Alih-alih menulis perulangan yang menyusuri objek dan array, Anda cukup mendeskripsikan lokasi yang Anda inginkan lewat sebuah path, lalu evaluator mengembalikan nilai yang cocok. Model berpikirnya sama dengan yang diberikan XPath untuk XML: sebuah path yang tersusun dari selektor yang melangkahi struktur.

JSONPath muncul di mana-mana saat developer berurusan dengan JSON. Anda memakainya untuk menarik sebuah field dari respons API, untuk membuat asersi atas sebuah nilai dalam integration test, untuk menyasar field di dalam konfigurasi pipeline pada Kubernetes, AWS Step Functions, dan Azure Logic Apps, serta untuk mengekstrak data dari JSON yang besar atau tidak beraturan tanpa menulis logika traversal secara manual.

Sedikit catatan sejarah, karena ini menjelaskan perpecahan dialeknya. Stefan Goessner mengusulkan JSONPath pada 2007. Ia menyebar cepat dan menjadi standar de facto, tetapi tidak pernah dispesifikasikan secara formal, sehingga implementasinya saling berbeda di detailnya. IETF menutup celah itu pada Februari 2024 lewat RFC 9535, spesifikasi JSONPath formal yang pertama. Kedua dialek masih hidup hingga kini, dan itulah sebabnya ekspresi yang sama bisa berperilaku berbeda tergantung library mana yang menjalankannya.

Sebelum mulai mengueri, ada baiknya membaca strukturnya dulu. Rapikan input yang berantakan dengan JSON Formatter agar susunan bersarangnya terlihat jelas.

Dokumen Contoh

Setiap contoh di bawah ini dijalankan terhadap JSON toko buku Goessner yang klasik. Tempel sekali saja dan pakai berulang:

{
  "store": {
    "book": [
      { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
      { "title": "Sword of Honour", "author": "Evelyn Waugh", "price": 12.99 },
      { "title": "Moby Dick", "author": "Herman Melville", "price": 8.99 },
      { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

Empat buku dengan title, author, dan price, ditambah satu bicycle. Ingat baik-baik: harganya adalah 8.95, 12.99, 8.99, dan 22.99, dan angka-angka itulah yang menentukan hasil filter nanti.

Root, Child, dan Penelusuran Rekursif ($ . ..)

Setiap ekspresi dimulai dari root, ditulis $. Dari sana Anda melangkah ke child dengan titik atau dengan notasi bracket, dan keduanya setara:

$.store.book          → the book array
$['store']['book']    → identical result

Notasi bracket adalah yang Anda butuhkan ketika sebuah key mengandung spasi, titik, atau karakter khusus lainnya: $['first name'] berfungsi di tempat yang membuat $.first name gagal.

Operator .. adalah penelusuran rekursif (recursive descent). Ia mencari di setiap level dokumen, bukan hanya child langsung, dan mengumpulkan segala sesuatu yang cocok dengan selektor berikutnya pada kedalaman berapa pun:

$..author
→ ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]

Kapan memakai .. versus path lengkap

Penelusuran rekursif memang praktis, tetapi tumpul. $..price mencocokkan setiap price di mana pun dalam pohon, termasuk store.bicycle.price, yang barangkali tidak Anda inginkan. Ketika Anda tahu bentuk datanya, eja path-nya secara lengkap agar kueri tetap presisi:

$..price                  → [8.95, 12.99, 8.99, 22.99, 19.95]  (includes the bicycle)
$.store.book[*].price     → [8.95, 12.99, 8.99, 22.99]         (only books)

Sisakan .. untuk struktur yang benar-benar tidak beraturan atau tidak diketahui. Semakin Anda paham bentuk datanya, semakin baik memilih path eksplisit ketimbang menebak lewat penelusuran rekursif.

Wildcard, Indeks, dan Slice Array (* [0] [start:end:step])

Wildcard * memilih semua elemen dari sebuah array atau semua anggota dari sebuah objek:

$.store.book[*].title
→ ["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]

Indeks array berbasis nol, dan indeks negatif menghitung dari ujung belakang:

$.store.book[0].title     → ["Sayings of the Century"]
$.store.book[-1].title    → ["The Lord of the Rings"]

Slice memakai konvensi setengah-terbuka [start:end:step] yang sama seperti Python dan JavaScript: start disertakan, end dikecualikan.

$.store.book[0:2].title   → ["Sayings of the Century", "Sword of Honour"]

Itu mengembalikan dua buku, bukan tiga: indeks 0 dan indeks 1, berhenti sebelum indeks 2. Batas akhir yang eksklusif adalah bug JSONPath yang paling sering muncul, jadi layak ditanamkan ke ingatan:

[0:2]   → first TWO elements (indices 0, 1)   ← correct
[0:3]   → first THREE elements (indices 0, 1, 2)

Hilangkan salah satu batas untuk berjalan sampai ujung, dan tambahkan step untuk mengambil setiap elemen ke-N:

$.store.book[2:].title    → ["Moby Dick", "The Lord of the Rings"]
$.store.book[:3].title    → first three titles
$.store.book[::2].title   → ["Sayings of the Century", "Moby Dick"]  (every other)

Ekspresi Filter [?()] — Bagian yang Paling Ampuh

Filter adalah bagian JSONPath yang paling banyak dipakai dalam praktik. Sebuah filter [?()] hanya menyimpan elemen yang predikatnya bernilai benar, dan di dalam filter, @ merujuk ke elemen yang sedang diuji.

Untuk memilih buku yang lebih murah dari 10:

$.store.book[?(@.price < 10)].title
→ ["Sayings of the Century", "Moby Dick"]

Terhadap harga-harga toko buku (8.95, 12.99, 8.99, 22.99), dua buku memenuhi syarat. Berikut cara membangun predikat filter langkah demi langkah:

  1. Bandingkan terhadap literal. Gunakan ==, !=, <, <=, >, >= — misalnya @.price > 10.
  2. Cocokkan sebuah string. Literal string memakai tanda kutip tunggal: @.author == 'Nigel Rees'.
  3. Uji keberadaan. Referensi anggota yang berdiri sendiri akan memilih elemen yang memilikinya: [?(@.isbn)] hanya menyimpan buku yang punya isbn.
  4. Gabungkan kondisi. Sambungkan predikat dengan && dan ||: [?(@.price < 10 && @.author == 'Herman Melville')].

Kesalahan filter yang paling sering adalah soal cakupan (scope) @. Di dalam predikat, elemen saat ini adalah @, bukan $. Menulis $.price justru menunjuk balik ke root dokumen, bukan ke buku yang sedang diuji:

$.store.book[?($.price < 10)]   → wrong scope, matches nothing useful
$.store.book[?(@.price < 10)]   → correct: each book's own price

RFC 9535 vs Classic di dalam filter

Kedua dialek berpisah jalan dalam hal spasi dan tanda kutip. Classic bersikap longgar, dan [?(@.price<10)] tanpa spasi pun terurai dengan baik. RFC 9535 mengikuti tata bahasanya secara persis dan lebih ketat soal cara penulisan filter. Jika sebuah filter yang berfungsi di tempat lain malah gagal, periksa spasi dan mesinnya. Jaga filter tetap bersih (operator diberi spasi, string dengan kutip tunggal) dan filter itu akan dievaluasi dengan cara yang sama, library mana pun yang akhirnya menjalankannya.

Selektor Union — Pilih Beberapa Key Sekaligus ([a,b])

Sebuah selektor union mendaftar beberapa nama atau indeks di dalam satu bracket dan mengumpulkan semuanya:

$.store.book[0]['title','author']
→ ["Sayings of the Century", "Nigel Rees"]

Union juga berfungsi dengan indeks, dan Anda bisa mencampurnya dengan selektor lain untuk proyeksi yang tetap:

$.store.book[0,2].title          → ["Sayings of the Century", "Moby Dick"]
$.store.book[*]['title','price']  → title and price of every book

Union adalah alat yang tepat ketika Anda menginginkan beberapa field tertentu, bukan keseluruhan objek atau sapuan wildcard.

Fungsi RFC 9535: length, count, match, search, value

RFC 9535 mendefinisikan lima ekstensi fungsi standar. Ada satu aturan yang sering keliru dipahami, termasuk oleh banyak panduan lain, yaitu ini:

Fungsi-fungsi ini hanya bisa dipanggil di dalam sebuah filter [?...], tidak pernah sebagai segmen path yang berdiri sendiri.

Menulis $.store.book.length() itu bukan RFC 9535 yang valid, dan tata bahasa standarnya menolaknya. Bentuk pemanggilan sebagai segmen itu adalah ekstensi jsonpath-plus, bukan bagian dari spesifikasi. Untuk memfilter berdasarkan panjang, Anda memanggil fungsinya di dalam predikat:

$.store.book[?length(@.title) > 15]
→ [
    { "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
    { "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
  ]

Kedua title yang terpilih lebih panjang dari 15 karakter; “Moby Dick” (9) dan “Sword of Honour” (15, jadi tidak lebih dari 15) dikecualikan.

Berikut yang dilakukan masing-masing fungsi di dalam sebuah filter:

  • length() — panjang sebuah string, array, atau objek: [?length(@.title) > 15]
  • count() — jumlah node dalam sebuah nodelist: [?(count(@.authors) > 1)]
  • match() — uji regex untuk seluruh string (pola I-Regexp): [?match(@.author, 'J.*')]
  • search() — uji regex untuk substring: [?search(@.title, 'the')]
  • value() — mengonversi nodelist satu-node menjadi nilainya untuk perbandingan

Kelimanya merupakan fitur RFC 9535. Dialek Classic (Goessner) tidak mengimplementasikannya, jadi jika sebuah ekspresi berbasis fungsi gagal, pastikan Anda memanggilnya di dalam sebuah filter dan mesin Anda disetel ke RFC 9535.

RFC 9535 vs Classic Goessner — Mengapa Ekspresi yang Sama Berbeda Hasilnya

Ketika sebuah ekspresi JSONPath memberi hasil berbeda di dua alat, biasanya dialeknya yang jadi penyebab. Berikut perbandingan keduanya:

AspekClassic Goessner (2007)RFC 9535 (2024)
StandardisasiDe facto, tidak pernah diformalkanSpesifikasi formal IETF pertama
Spasi/kutip pada filterLonggar ([?(@.price<10)] boleh)Ketat, mengikuti tata bahasa persis
Perbandingan anggota yang hilangDitentukan implementasiTerdefinisi jelas, tidak melempar error
Fungsi standarBukan bagian dialeklength count match search value
Normalized pathsTidak ada bentuk kanonisKanonis, bentuk bracket berkutip tunggal
Pengurutan unionBervariasi per libraryDispesifikasikan

Saran praktis: jika sistem hilir Anda mengiklankan kepatuhan terhadap RFC 9535, tulis dan validasi terhadap mesin standar. Jika Anda memelihara ekspresi yang disalin dari jsonpath.com, jsonpath-plus, atau layanan berbasis Jayway, pakai Classic agar hasilnya bisa direproduksi. JSONPath Tester menjalankan kedua mesin di balik satu sakelar, sehingga Anda bisa menempel sebuah ekspresi sekali saja dan melihat bagaimana tiap dialek menanganinya berdampingan. Membandingkan kedua mesin seperti ini biasanya cara tercepat untuk mendiagnosis perbedaan hasil.

JSONPath vs XPath vs jq — Mana yang Dipakai

Ketiganya sering tertukar, jadi inilah versi singkatnya:

  • JSONPath adalah kueri path deklaratif untuk JSON. Ia paling cocok disematkan di dalam konfigurasi dan asersi pengujian, ketika Anda ingin menyebut sebuah lokasi tanpa menulis kode.
  • XPath adalah padanannya di dunia XML. JSONPath meminjam sebagian notasinya (*, .., []), itulah sebabnya analoginya berlaku, tetapi kedua bahasa ini tidak saling bisa dipertukarkan dan kumpulan fungsinya berbeda.
  • jq adalah pemroses JSON di baris perintah (command-line). Ia jauh melampaui sekadar pemilihan path — transformasi, agregasi, penataan ulang — dan hidup di dalam pipeline shell Anda.

Keputusannya biasanya jelas. Untuk asersi yang disematkan atau sebuah field konfigurasi pipeline, pakai JSONPath. Untuk transformasi dan pengolahan data berbasis shell, pakai jq; jq Cheat Sheet membahas alur kerja itu secara mendalam. Dan ketika pertanyaannya adalah apakah sebuah payload sesuai dengan bentuk yang diharapkan, bukan di mana sebuah field berada, validasilah dengan JSON Schema Validator beserta panduan validasi lengkapnya.

7 Kesalahan JSONPath yang Umum

  1. Lupa root $. store.book ditolak oleh sebagian besar mesin; setiap ekspresi dimulai dari $.
  2. Slice meleset satu (off-by-one). [0:2] adalah dua elemen, bukan tiga; batas akhirnya eksklusif.
  3. Dialek keliru. Menjalankan ekspresi Classic di bawah RFC 9535 (atau sebaliknya) bisa memicu parse-error atau mencocokkan node yang berbeda. Ganti mesin agar sesuai.
  4. Fungsi sebagai segmen yang berdiri sendiri. $.store.book.length() tidak valid di RFC 9535; panggil length() di dalam sebuah filter.
  5. Lupa @ di dalam filter. [?($.price < 10)] menunjuk ke root; gunakan [?(@.price < 10)].
  6. Kutip bracket keliru. $[store] adalah sebuah error; beri tanda kutip pada key: $['store'].
  7. Mengira .. berhenti di level pertama. Penelusuran rekursif mencocokkan pada setiap kedalaman, bukan hanya child langsung.

Uji JSONPath Online, Secara Privat

Cara tercepat untuk mempelajari sintaks JSONPath adalah dengan menjalankannya. JSONPath Tester mengevaluasi setiap ekspresi dalam panduan ini secara langsung: mesin ganda RFC 9535 dan Classic, tampilan hasil Values / Paths / Both, normalized paths untuk debugging, dan eksekusi 100% di dalam browser, tanpa unggahan, tanpa pendaftaran, dan tanpa eval, sehingga aman untuk payload milik sendiri. Susun sebuah path di sini, pastikan ia memilih persis node yang Anda inginkan, lalu tempel ekspresi yang sudah tervalidasi itu langsung ke dalam kode, pengujian, atau pipeline Anda.

Untuk sisa alur kerja JSON, ubah sebuah respons contoh menjadi interface bertipe dengan JSON to TypeScript, atau bandingkan dua dokumen field demi field dengan JSON Diff.

Pertanyaan yang Sering Diajukan

Untuk apa JSONPath digunakan?

JSONPath mengueri JSON tanpa kode imperatif. Developer memakainya untuk menarik field dari respons API, membuat asersi atas nilai dalam integration test, dan menyasar field di konfigurasi untuk Kubernetes, AWS Step Functions, dan Azure Logic Apps. Ia unggul dalam mengekstrak data dari struktur yang besar atau tidak beraturan, yang menulis traversal-nya secara manual akan merepotkan.

Apa beda antara RFC 9535 dan JSONPath klasik?

Classic adalah dialek de facto buatan Stefan Goessner tahun 2007, diimplementasikan secara luas tetapi tidak pernah dispesifikasikan formal, sehingga library-library-nya saling berbeda. RFC 9535 adalah spesifikasi formal IETF dari Februari 2024: ia mendefinisikan tata bahasa yang presisi, normalized paths untuk hasil, dan lima fungsi standar. Keduanya berbeda di kasus tepi pada filter, union, dan perbandingan anggota yang hilang.

Bagaimana cara kerja ekspresi filter JSONPath?

Sebuah filter [?()] hanya menyimpan elemen yang predikatnya benar, dan @ adalah elemen saat ini. Misalnya, $.store.book[?(@.price < 10)] memilih buku berharga di bawah 10. Anda bisa menggabungkan kondisi dengan && dan ||, menguji apakah suatu anggota ada, dan membandingkan terhadap literal string atau angka.

Bisakah saya memakai length() sebagai $.store.book.length()?

Tidak. Dalam RFC 9535, length() dan keempat fungsi lainnya hanya bisa dipanggil di dalam sebuah filter, seperti $.store.book[?length(@.title) > 15]. Bentuk segmen yang berdiri sendiri $.store.book.length() adalah ekstensi jsonpath-plus, bukan JSONPath standar, dan tata bahasa RFC 9535 menolaknya.

Apakah JSONPath sama dengan XPath?

Tidak, tetapi idenya serupa. XPath mengueri XML; JSONPath mengueri JSON; keduanya melokalisasi node dengan selektor path. JSONPath sengaja meminjam sebagian notasi XPath — *, .., dan [] — yang membuat analoginya berguna, tetapi sintaks, semantik, dan kumpulan fungsinya berbeda dan tidak saling bisa dipertukarkan.

Apa yang dilakukan penelusuran rekursif (..) dalam JSONPath?

Operator .. mencari di setiap level dokumen, bukan hanya child langsung. $..author mengumpulkan setiap anggota author di mana pun ia muncul, pada kedalaman bersarang berapa pun. Ia adalah cara tercepat untuk menarik satu field dari struktur yang bersarang dalam atau tidak beraturan, tetapi ia bisa mencocokkan jauh lebih banyak node dari yang Anda kira, jadi persempit bila memungkinkan.

Tag: jsonpath json query-language rfc-9535 filter-expression developer-tools

Artikel Terkait

Lihat semua artikel