JMESPath untuk Pemula: Pertanyaan Unjuran yang Mengalahkan JSONPath
Inti pati: JMESPath membentuk semula kumpulan objek dengan lebih langsung daripada JSONPath. Panduan pemula ini merangkumi sintaks, unjuran, paip, fungsi terbina dalam dan penggunaan AWS CLI.

Intinya: gunakan JSON Pointer untuk mencari mengikut kedudukan, JSONPath untuk menapis atau membuat rekursi, dan JMESPath apabila anda perlu membentuk semula hasil atau mengunjurkannya pada baris arahan; satu medan yang diketahui tidak memerlukan kedua-duanya.
Jika matlamat anda adalah membentuk semula sekumpulan objek kepada bentuk yang lain, JMESPath selalunya lebih langsung daripada JSONPath. Ia tidak mencari nod; ia membina hasil.
1. Apa Itu JMESPath
JMESPath ialah bahasa pertanyaan yang direka khusus untuk JSON. Ia pada mulanya dicipta untuk menapis output AWS CLI dan kemudian tersebar ke banyak bahasa melalui pelaksanaan seperti jmespath.js, jmespath.py, dan go-jmespath. Matlamatnya berbeza daripada JSONPath:
- JSONPath memberi tumpuan kepada memilih satu set nod mengikut laluan dan syarat, serta memulangkan satu set nod;
- JMESPath memberi tumpuan kepada mengubah input JSON dan memulangkan nilai dalam sebarang bentuk.
Perbezaan ini menjadikan JMESPath sangat baik untuk unjuran senarai, penamaan semula medan, dan pemangkasan hasil, seperti mengurangkan dua puluh medan daripada respons API kepada tiga medan yang benar-benar diperlukan oleh frontend. Jika anda belum membaca asas JSONPath, mulakan dengan Bahasa Pertanyaan JSONPath dalam Amalan.
2. Sintaks Asas: Daripada Pengenal kepada Indeks
JMESPath menggunakan lebih sedikit simbol daripada JSONPath dan lebih mirip ungkapan pengekstrakan:
| Sintaks | Maksud | Contoh |
|---|---|---|
name |
Akses anggota objek | people |
a.b |
Akses anggota bersarang | store.bicycle.color |
[0] |
Akses indeks tatasusunan | people[0] |
[-1] |
Akses elemen terakhir | people[-1] |
[0:2] |
Hirisan, permulaan inklusif dan penghujung eksklusif | people[0:2] |
[*] |
Unjuran senarai | people[*].name |
[] |
Unjuran perataan | people[].tags[] |
[?condition] |
Unjuran penapis | people[?age > 18] |
{key: expr} |
Cincangan pilih berbilang | {name: name, age: age} |
[expr, expr] |
Senarai pilih berbilang | [name, age] |
| |
Salurkan hasil kiri ke bahagian kanan | people[*].age | [0] |
Contoh minimum:
{
"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 memulangkan tatasusunan: hirisan memulangkan senarai, dan .name berikutnya secara automatik diunjurkan ke atas setiap elemen dalam senarai itu. Ini adalah perbezaan terbesar antara JMESPath dan akses objek biasa.
3. Unjuran: Menukar Satu Senarai Menjadi Senarai Lain
Unjuran ialah konsep teras dalam JMESPath. Ia berfungsi dalam dua langkah: nilaikan setiap elemen senarai, kemudian kumpulkan hasilnya ke dalam senarai baharu. Elemen yang bernilai null digugurkan secara automatik.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // a list of objects
people[?age > 18].name // ["Alice"]
Baris kedua ialah ungkapan unjuran biasa: people[*].{name: name, age: age}. Ia membina semula setiap elemen sebagai { "name": ..., "age": ... } dan membuang medan city yang berlebihan. Berbanding JSONPath, yang memberikan nod yang kemudian anda petakan dalam kod, ini menjimatkan satu gelung.
Dua jenis unjuran wajar dibezakan:
[*]mengekalkan struktur senarai asal dan melangkau nilai null;[]ialah unjuran perataan yang mula-mula meruntuhkan satu tahap senarai bersarang, sesuai untuk ungkapan sepertipeople[].tags[]yang memerlukan "semua tag semua orang".
4. Paip dan Pilih Berbilang
Paip | bermaksud "hentikan unjuran semasa dan layan hasilnya sebagai input baharu". Penggunaannya yang paling biasa ialah mengambil indeks selepas unjuran:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → the maximum age
Tanpa paip, people[*].name[0] ditafsirkan sebagai mengambil aksara pertama setiap nama, yang bukan apa yang anda mahu. Paip ialah simbol yang paling mudah terlepas pandang dan paling berguna dalam JMESPath.
Terdapat dua bentuk pilih berbilang:
// 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]
Cincangan pilih berbilang baik untuk membina hasil bernama, manakala senarai pilih berbilang baik untuk penggunaan mengikut kedudukan. Kedua-duanya boleh disarangkan di dalam unjuran, tetapi ambil perhatian: dalam cincangan pilih berbilang, kunci yang nilainya null tetap dikekalkan, sedangkan unjuran menggugurkan keseluruhan elemen null.
5. Fungsi Terbina Dalam
Standard JMESPath mentakrifkan satu set fungsi. Yang biasa termasuk:
| Fungsi | Tujuan | Contoh |
|---|---|---|
length() |
Dapatkan panjang | length(people) |
sort() |
Susun nilai | sort(people[*].age) |
sort_by() |
Susun mengikut medan | sort_by(people, &age) |
max() / min() |
Maksimum / minimum | max(people[*].age) |
sum() |
Jumlah nilai | sum(people[*].age) |
keys() |
Dapatkan kunci objek | keys(people[0]) |
contains() |
Semak kandungan | contains(name, "Ali") |
to_string() |
Tukar kepada rentetan | to_string(age) |
Argumen kedua sort_by ialah ungkapan &, bermaksud "ambil medan ini daripada setiap elemen":
sort_by(people, &age)[*].name
Fungsi-fungsi ini membolehkan JMESPath melakukan pengagregatan mudah, sedangkan JSONPath direka hanya untuk pemilihan; penjumlahan dan pengelompokan biasanya memerlukan jq atau kod.
6. Perbandingan Bersebelahan dengan JSONPath
| Dimensi | JSONPath | JMESPath |
|---|---|---|
| Matlamat utama | Pilih nod | Ubah data |
| Simbol akar | $ |
Tiada; tulis nama medan secara langsung |
| Nod semasa | @ |
@ (hanya dalam argumen fungsi) |
| Penapis | [?(@.age > 18)] |
[?age > 18] |
| Kad liar | [*] |
[*] atau [] |
| Bina objek baharu | Tidak disokong | {name: name} |
| Paip | Tidak disokong | | |
| Standard | RFC 9535 | Spesifikasi JMESPath |
Panduan keputusan satu ayat:
- Gunakan JSONPath apabila anda hanya perlu mencari nod yang sepadan dengan syarat;
- Gunakan JMESPath apabila anda perlu membentuk semula hasil kepada bentuk yang dijangka oleh frontend;
- Gunakan kedua-duanya sekali pun tidak apabila anda membaca satu medan yang diketahui; cukup akses sifat secara langsung.
Jika hasil pertanyaan mesti ditulis kembali ke dokumen, kedua-dua bahasa ini tidak sesuai. Gunakan JSON Pointer dan patch sebaliknya; lihat JSON Pointer, Patch, dan Merge Patch.
7. JMESPath dalam AWS CLI
Parameter --query AWS CLI ialah JMESPath, iaitu penggunaan paling meluasnya:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
Arahan ini melakukan tiga perkara: ia meratakan dua senarai bersarang dengan [], mengekalkan hanya tiga medan dengan cincangan pilih berbilang, dan memaparkan hasil sebagai jadual dengan --output table. Dengan JSONPath anda memerlukan lebih banyak tahap dan tidak akan mendapat kesan "namakan semula serta pangkas".
Bentuk biasa yang lain:
# Only running instances
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# The value of the Name tag
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Perhatikan bahawa tanda belakang mengepit literal, bukan rentetan. Dalam AWS CLI, rentetan menggunakan petikan tunggal dan literal JSON menggunakan tanda belakang. Perbezaan ini ialah salah satu kesilapan pemula yang paling biasa.
8. Kesilapan Biasa
- Mengelirukan
[*]dan[]: yang pertama mengekalkan penyarangan, yang kedua meratakan satu tahap, dan struktur yang terhasil sangat berbeza; - Terlupa paip:
people[*].name[0]mengambil aksara pertama setiap nama, bukan nama pertama; - Menulis
@di dalam penapis: penapis JMESPath menggunakan nama medan secara langsung, jadi[?@.age > 18]adalah salah; - Menganggap
nullsebagai rentetan kosong: unjuran menggugurkannull, tetapi cincangan pilih berbilang mengekalkan kunci; - Menggunakan petikan berganda pada literal dalam AWS CLI: shell mengembangkannya dahulu dan menulis semula ungkapan itu.
Nasihat penyahpepijatan: sahkan ungkapan pada input sekecil mungkin sebelum menerapkannya pada data sebenar, dan tambah --output json dalam AWS CLI untuk melihat hasil mentah. Untuk penyuntingan dan pemformatan JSON harian, berganding dengan Penyunting JSON dan Alat Baris Arahan menjadikan pencarian laluan medan lebih pantas.
FAQ
Bolehkah JMESPath dan JSONPath digunakan bersama?
Ya, tetapi biasanya tidak perlu. Pendekatan biasa ialah memangkas objek kecil dengan JMESPath, kemudian menggunakan akses sifat biasa dalam kod. Jika anda benar-benar perlu mencari mengikut laluan dan kemudian mengubah, JSONPath membaca dan JSON Pointer menulis, yang merupakan pembahagian tugas yang lebih jelas.
Mengapa people[*].name memulangkan sesuatu selain rentetan tunggal?
Kerana [*] ialah unjuran, dan hasilnya sentiasa senarai, walaupun hanya ada satu orang. Untuk mendapatkan satu nilai, tambah paip dan indeks: people[*].name | [0].
Apa yang berlaku kepada null di dalam unjuran?
Unjuran senarai secara automatik melangkau elemen yang bernilai null, jadi hasilnya mungkin lebih pendek daripada senarai asal. Cincangan pilih berbilang mengekalkan kunci dan memelihara nilai null, yang penting semasa membina respons API.
Bolehkah JMESPath melakukan pengagregatan dan pengelompokan?
Ia boleh melakukan pengagregatan asas dengan sum(), max(), dan sort_by(). Pengelompokan, cantuman, dan fungsi tetingkap berada di luar standard. Serahkan analisis kompleks kepada jq, SQL, atau kod.
Bagaimana saya memutuskan antara JMESPath dan JSONPath?
Gunakan JSONPath apabila anda perlu memilih nod mengikut laluan dan syarat serta menerima satu set nod; gunakan JMESPath apabila anda perlu membentuk semula hasil kepada bentuk yang dijangka oleh frontend, dengan penamaan semula dan pemangkasan medan. Apabila anda membaca satu medan yang diketahui, gunakan kedua-duanya sekali pun tidak; cukup akses sifatnya.
Patutkah saya menggunakan paip untuk mengambil nilai unjuran pertama?
Ya. people[*].name ialah unjuran, jadi hasilnya sentiasa senarai; tanpa paip, people[*].name[0] mengambil aksara pertama setiap nama. Tulis people[*].name | [0] untuk mendapatkan nilai pertama.