JMESPath untuk Pemula: Query Proyeksi yang Mengungguli JSONPath
Intinya: JMESPath membentuk ulang kumpulan objek lebih langsung daripada JSONPath. Panduan pemula ini mencakup sintaks, proyeksi, pipe, fungsi bawaan, dan penggunaan AWS CLI.

Intinya: gunakan JSON Pointer untuk melokasikan berdasarkan posisi, JSONPath untuk memfilter atau rekursi, dan JMESPath ketika Anda perlu membentuk ulang hasil atau memproyeksikannya di command line; satu field yang sudah diketahui tidak memerlukan keduanya.
Jika tujuan Anda adalah membentuk ulang sekumpulan objek menjadi bentuk lain, JMESPath sering kali lebih langsung daripada JSONPath. Ia tidak mencari node; ia mengonstruksi hasil.
1. Apa Itu JMESPath
JMESPath adalah bahasa query yang dirancang khusus untuk JSON. Awalnya dibuat untuk memfilter output AWS CLI dan kemudian menyebar ke banyak bahasa melalui implementasi seperti jmespath.js, jmespath.py, dan go-jmespath. Tujuannya berbeda dari JSONPath:
- JSONPath berfokus memilih sekumpulan node berdasarkan path dan kondisi, serta mengembalikan himpunan node;
- JMESPath berfokus mentransformasi JSON masukan dan mengembalikan nilai dengan bentuk apa pun.
Perbedaan ini membuat JMESPath sangat baik untuk proyeksi list, penggantian nama field, dan pemangkasan hasil, misalnya mereduksi dua puluh field dari respons API menjadi tiga field yang benar-benar dibutuhkan frontend. Jika Anda belum membaca dasar-dasar JSONPath, mulailah dari Bahasa Query JSONPath dalam Praktik.
2. Sintaks Dasar: Dari Identifier ke Indeks
JMESPath menggunakan lebih sedikit simbol daripada JSONPath dan lebih terbaca seperti ekspresi ekstraksi:
| Sintaks | Arti | Contoh |
|---|---|---|
name |
Mengakses anggota objek | people |
a.b |
Mengakses anggota bersarang | store.bicycle.color |
[0] |
Mengakses indeks array | people[0] |
[-1] |
Mengakses elemen terakhir | people[-1] |
[0:2] |
Slice, awal inklusif dan akhir eksklusif | people[0:2] |
[*] |
Proyeksi list | people[*].name |
[] |
Proyeksi flatten | people[].tags[] |
[?condition] |
Proyeksi filter | people[?age > 18] |
{key: expr} |
Multi-select hash | {name: name, age: age} |
[expr, expr] |
Multi-select list | [name, age] |
| |
Menyalurkan hasil kiri ke sisi kanan | people[*].age | [0] |
Contoh minimal:
{
"people": [
{ "name": "Alice", "age": 31, "city": "Shanghai" },
{ "name": "Bob", "age": 17, "city": "Beijing" }
]
}
people[0].name → "Alice"
people[-1].city → "Beijing"
people[0:2].name → ["Alice", "Bob"]
Perhatikan mengapa people[0:2].name mengembalikan array: slice mengembalikan list, dan .name berikutnya otomatis diproyeksikan ke setiap elemen dalam list tersebut. Inilah perbedaan terbesar antara JMESPath dan akses objek biasa.
3. Proyeksi: Mengubah Satu List Menjadi List Lain
Proyeksi adalah konsep inti dalam JMESPath. Ia bekerja dalam dua langkah: evaluasi setiap elemen list, lalu kumpulkan hasilnya menjadi list baru. Elemen yang dievaluasi menjadi null otomatis dibuang.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // a list of objects
people[?age > 18].name // ["Alice"]
Baris kedua adalah ekspresi proyeksi khas: people[*].{name: name, age: age}. Ia membangun ulang setiap elemen menjadi { "name": ..., "age": ... } dan membuang field city yang berlebih. Dibandingkan dengan JSONPath, yang memberi Anda node lalu Anda petakan di kode, ini menghemat satu loop.
Dua jenis proyeksi perlu dibedakan:
[*]mempertahankan struktur list asli dan melewati nilai null;[]adalah proyeksi flatten yang lebih dulu meruntuhkan satu level list bersarang, cocok untuk ekspresi sepertipeople[].tags[]yang membutuhkan "semua tag dari semua orang".
4. Pipe dan Multi-Select
Pipe | berarti "hentikan proyeksi saat ini dan perlakukan hasilnya sebagai masukan baru". Penggunaan paling umumnya adalah mengambil indeks setelah proyeksi:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → the maximum age
Tanpa pipe, people[*].name[0] ditafsirkan sebagai mengambil karakter pertama dari setiap nama, yang bukan yang Anda inginkan. Pipe adalah simbol yang paling mudah terlewatkan sekaligus paling berguna di JMESPath.
Ada dua bentuk multi-select:
// Multi-select hash: the result is an object
people[0].{name: name, age: age}
// Multi-select list: the result is an array
people[0].[name, age]
Multi-select hash cocok untuk mengonstruksi hasil bernama, sedangkan multi-select list cocok untuk konsumsi posisional. Keduanya dapat disarangkan di dalam proyeksi, tetapi perhatikan: dalam multi-select hash, key yang nilainya null tetap dipertahankan, sementara proyeksi membuang seluruh elemen null.
5. Fungsi Bawaan
Standar JMESPath mendefinisikan sekumpulan fungsi. Yang umum meliputi:
| Fungsi | Tujuan | Contoh |
|---|---|---|
length() |
Mendapatkan panjang | length(people) |
sort() |
Mengurutkan nilai | sort(people[*].age) |
sort_by() |
Mengurutkan berdasarkan field | sort_by(people, &age) |
max() / min() |
Maksimum / minimum | max(people[*].age) |
sum() |
Menjumlahkan nilai | sum(people[*].age) |
keys() |
Mendapatkan key objek | keys(people[0]) |
contains() |
Memeriksa keberadaan | contains(name, "Ali") |
to_string() |
Mengonversi ke string | to_string(age) |
Argumen kedua sort_by adalah ekspresi &, yang berarti "ambil field ini dari setiap elemen":
sort_by(people, &age)[*].name
Fungsi-fungsi ini membuat JMESPath mampu melakukan agregasi sederhana, sedangkan JSONPath hanya dirancang untuk seleksi; penjumlahan dan pengelompokan biasanya membutuhkan jq atau kode.
6. Perbandingan Berdampingan dengan JSONPath
| Dimensi | JSONPath | JMESPath |
|---|---|---|
| Tujuan utama | Memilih node | Mentransformasi data |
| Simbol akar | $ |
Tidak ada; tulis nama field secara langsung |
| Node saat ini | @ |
@ (hanya dalam argumen fungsi) |
| Filter | [?(@.age > 18)] |
[?age > 18] |
| Wildcard | [*] |
[*] atau [] |
| Mengonstruksi objek baru | Tidak didukung | {name: name} |
| Pipe | Tidak didukung | | |
| Standar | RFC 9535 | Spesifikasi JMESPath |
Panduan keputusan dalam satu kalimat:
- Gunakan JSONPath ketika Anda hanya perlu menemukan node yang cocok dengan suatu kondisi;
- Gunakan JMESPath ketika Anda perlu membentuk ulang hasil menjadi seperti yang diharapkan frontend;
- Gunakan keduanya jika tidak perlu ketika Anda membaca satu field yang sudah diketahui; akses propertinya secara langsung.
Jika hasil query harus ditulis balik ke dokumen, kedua bahasa ini tidak cocok. Gunakan JSON Pointer dan patch sebagai gantinya; lihat JSON Pointer, Patch, dan Merge Patch.
7. JMESPath di AWS CLI
Parameter --query pada AWS CLI adalah JMESPath, yang merupakan penggunaan paling luas dari bahasa ini:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
Perintah ini melakukan tiga hal: meratakan dua list bersarang dengan [], mempertahankan hanya tiga field dengan multi-select hash, dan merender hasilnya sebagai tabel dengan --output table. Dengan JSONPath Anda akan membutuhkan lebih banyak level dan tidak akan mendapatkan efek "ganti nama plus pangkas".
Bentuk umum lainnya:
# Hanya instance yang berjalan
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# Nilai tag Name
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Perhatikan bahwa backtick mengapit literal, bukan string. Di AWS CLI, string menggunakan tanda kutip tunggal dan literal JSON menggunakan backtick. Perbedaan ini adalah salah satu kesalahan pemula yang paling umum.
8. Kesalahan Umum
- Mencampuradukkan
[*]dan[]: yang pertama mempertahankan penyarangan, yang kedua meratakan satu level, dan struktur hasilnya benar-benar berbeda; - Lupa pipe:
people[*].name[0]mengambil karakter pertama dari setiap nama, bukan nama pertama; - Menulis
@di dalam filter: filter JMESPath menggunakan nama field secara langsung, jadi[?@.age > 18]salah; - Menganggap
nullsebagai string kosong: proyeksi membuangnull, tetapi multi-select hash mempertahankan key; - Menggunakan tanda kutip ganda untuk literal di AWS CLI: shell akan memekarkannya lebih dulu dan menulis ulang ekspresinya.
Saran debugging: validasi ekspresi pada masukan sekecil mungkin sebelum menerapkannya ke data nyata, dan tambahkan --output json di AWS CLI untuk melihat hasil mentah. Untuk pengeditan dan pemformatan JSON sehari-hari, memadukannya dengan Editor JSON dan Alat Command-Line membuat pelokasian path field jauh lebih cepat.
FAQ
Apakah JMESPath dan JSONPath dapat digunakan bersama?
Ya, tetapi biasanya tidak perlu. Pendekatan umum adalah memangkas objek kecil dengan JMESPath, lalu menggunakan akses properti biasa di kode. Jika Anda benar-benar perlu melokasikan berdasarkan path lalu memodifikasi, JSONPath membaca dan JSON Pointer menulis, yang merupakan pembagian kerja lebih jelas.
Mengapa people[*].name mengembalikan sesuatu selain satu string?
Karena [*] adalah proyeksi, dan hasilnya selalu list, bahkan ketika hanya ada satu orang. Untuk mendapatkan satu nilai, tambahkan pipe dan indeks: people[*].name | [0].
Apa yang terjadi pada null di dalam proyeksi?
Proyeksi list otomatis melewati elemen yang dievaluasi menjadi null, sehingga hasilnya bisa lebih pendek daripada list asli. Multi-select hash mempertahankan key dan menjaga nilai null, yang penting saat mengonstruksi respons API.
Apakah JMESPath dapat melakukan agregasi dan pengelompokan?
Ia dapat melakukan agregasi dasar dengan sum(), max(), dan sort_by(). Pengelompokan, join, dan window function berada di luar standar. Serahkan analisis kompleks kepada jq, SQL, atau kode.
Bagaimana saya memutuskan antara JMESPath dan JSONPath?
Gunakan JSONPath ketika Anda perlu memilih node berdasarkan path dan kondisi serta menerima himpunan node; gunakan JMESPath ketika Anda perlu membentuk ulang hasil menjadi seperti yang diharapkan frontend, dengan penggantian nama dan pemangkasan field. Ketika Anda membaca satu field yang sudah diketahui, gunakan keduanya tidak perlu; cukup akses propertinya.
Haruskah saya menggunakan pipe untuk mengambil nilai proyeksi pertama?
Ya. people[*].name adalah proyeksi, jadi hasilnya selalu list; tanpa pipe, people[*].name[0] mengambil karakter pertama dari setiap nama. Tulis people[*].name | [0] untuk mendapatkan nilai pertama.