JMESPath для початківців: проєкційні запити, що кращі за JSONPath
Головне: JMESPath переформовує партії обʼєктів безпосередніше за JSONPath. Цей посібник для початківців охоплює синтаксис, проєкції, конвеєри, вбудовані функції й використання в AWS CLI.

Коротко: використовуйте JSON Pointer для локалізації за позицією, JSONPath для фільтрації чи рекурсії, а JMESPath — коли треба переформувати результат або проєктувати його в командному рядку; одне відоме поле не потребує жодного з них.
Якщо ваша мета — переформувати партію обʼєктів в іншу форму, JMESPath часто безпосередніший за JSONPath. Він не шукає вузли; він конструює результат.
1. Що таке JMESPath
JMESPath — мова запитів, розроблена спеціально для JSON. Спочатку вона створювалася для фільтрації виводу AWS CLI, а згодом поширилася багатьма мовами через реалізації на кшталт jmespath.js, jmespath.py і go-jmespath. Її мета відрізняється від JSONPath:
- JSONPath зосереджується на виборі набору вузлів за шляхом і умовою та повертає набір вузлів;
- JMESPath зосереджується на перетворенні вхідного JSON і повертає значення будь-якої форми.
Ця різниця робить JMESPath особливо добрим для проєкцій списків, перейменування полів і обрізання результатів, як-от зведення двадцяти полів із відповіді API до трьох, які фронтенду справді потрібні. Якщо ви ще не читали основи JSONPath, почніть із Мова запитів JSONPath на практиці.
2. Базовий синтаксис: від ідентифікаторів до індексів
JMESPath використовує менше символів, ніж JSONPath, і читається більше як вираз вилучення:
| Синтаксис | Значення | Приклад |
|---|---|---|
name |
Доступ до члена обʼєкта | people |
a.b |
Доступ до вкладених членів | store.bicycle.color |
[0] |
Доступ до індексу масиву | people[0] |
[-1] |
Доступ до останнього елемента | people[-1] |
[0:2] |
Зріз, включний початок і виключний кінець | people[0:2] |
[*] |
Проєкція списку | people[*].name |
[] |
Проєкція вирівнювання | people[].tags[] |
[?condition] |
Проєкція фільтра | people[?age > 18] |
{key: expr} |
Хеш із кількома виборами | {name: name, age: age} |
[expr, expr] |
Список із кількома виборами | [name, age] |
| |
Передати лівий результат у праву сторону | people[*].age | [0] |
Мінімальний приклад:
{
"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"]
Зверніть увагу, чому people[0:2].name повертає масив: зріз повертає список, а наступний .name автоматично проєктується на кожен елемент цього списку. Це найбільша різниця між JMESPath і звичайним доступом до обʼєкта.
3. Проєкції: перетворення одного списку на інший
Проєкція — основний концепт JMESPath. Вона працює у два кроки: обчислити кожен елемент списку, потім зібрати результати в новий список. Елементи, що обчислюються в null, відкидаються автоматично.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // список обʼєктів
people[?age > 18].name // ["Alice"]
Другий рядок — типовий проєкційний вираз: people[*].{name: name, age: age}. Він перебудовує кожен елемент як { "name": ..., "age": ... } і відкидає зайве поле city. Порівняно з JSONPath, що дає вузли, які ви потім мапите в коді, це заощаджує цикл.
Варто розрізняти два види проєкцій:
[*]зберігає оригінальну структуру списку й пропускає null-значення;[]— проєкція вирівнювання, що спершу згортає один рівень вкладених списків, підходить для виразів на кшталтpeople[].tags[], яким потрібні «усі теги всіх людей».
4. Конвеєри й множинний вибір
Конвеєр | означає «зупинити поточну проєкцію й трактувати результат як новий вхід». Його найчастіше використовують, щоб узяти індекс після проєкції:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → максимальний вік
Без конвеєра people[*].name[0] трактується як узяття першого символу кожної назви, що не те, що ви хочете. Конвеєр — найлегше проґавлений і найкорисніший символ у JMESPath.
Є дві форми множинного вибору:
// Хеш із кількома виборами: результат — обʼєкт
people[0].{name: name, age: age}
// Список із кількома виборами: результат — масив
people[0].[name, age]
Хеші з кількома виборами добрі для побудови іменованих результатів, а списки з кількома виборами — для позиційного споживання. Обидва можна вкладати в проєкції, але зауважте: у хеші з кількома виборами ключ зі значенням null усе ще зберігається, тоді як проєкція відкидає весь null-елемент.
5. Вбудовані функції
Стандарт JMESPath визначає набір функцій. Поширені:
| Функція | Призначення | Приклад |
|---|---|---|
length() |
Отримати довжину | length(people) |
sort() |
Сортувати значення | sort(people[*].age) |
sort_by() |
Сортувати за полем | sort_by(people, &age) |
max() / min() |
Максимум / мінімум | max(people[*].age) |
sum() |
Підсумувати значення | sum(people[*].age) |
keys() |
Отримати ключі обʼєкта | keys(people[0]) |
contains() |
Перевірити входження | contains(name, "Ali") |
to_string() |
Перетворити на рядок | to_string(age) |
Другий аргумент sort_by — вираз &, що означає «взяти це поле з кожного елемента»:
sort_by(people, &age)[*].name
Ці функції дають JMESPath змогу виконувати просту агрегацію, тоді як JSONPath призначений лише для вибору; підсумовування й групування зазвичай потребують jq або коду.
6. Порівняння пліч-о-пліч із JSONPath
| Вимір | JSONPath | JMESPath |
|---|---|---|
| Основна мета | Вибирати вузли | Перетворювати дані |
| Символ кореня | $ |
Немає; пишіть назви полів безпосередньо |
| Поточний вузол | @ |
@ (лише в аргументах функцій) |
| Фільтр | [?(@.age > 18)] |
[?age > 18] |
| Шаблон | [*] |
[*] або [] |
| Побудова нових обʼєктів | Не підтримується | {name: name} |
| Конвеєри | Не підтримуються | | |
| Стандарт | RFC 9535 | Специфікація JMESPath |
Путівник рішення одним реченням:
- Використовуйте JSONPath, коли треба лише знайти вузли, що відповідають умові;
- Використовуйте JMESPath, коли треба переформувати результат у те, що очікує фронтенд;
- Не використовуйте жодного, коли читаєте одне відоме поле; просто зверніться до властивості безпосередньо.
Якщо результат запиту треба записати назад у документ, жодна мова не підходить. Використовуйте JSON Pointer і патчі; дивіться JSON Pointer, Patch і Merge Patch.
7. JMESPath в AWS CLI
Параметр --query в AWS CLI — це JMESPath, що є найпоширенішим випадком його використання:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
Ця команда робить три речі: вирівнює два вкладені списки через [], зберігає лише три поля через хеш із кількома виборами й відображає результат як таблицю через --output table. З JSONPath вам знадобилося б більше рівнів і ви не отримали б ефекту «перейменувати плюс обрізати».
Інші поширені форми:
# Лише запущені екземпляри
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# Значення тегу Name
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Зауважте, що зворотні лапки охоплюють літерали, а не рядки. В AWS CLI рядки використовують одинарні лапки, а літерали JSON — зворотні. Ця різниця — одна з найпоширеніших помилок початківців.
8. Поширені помилки
- Плутанина
[*]і[]: перше зберігає вкладеність, друге вирівнює один рівень, а отримані структури цілком різні; - Забутий конвеєр:
people[*].name[0]бере перший символ кожної назви, а не першу назву; - Написання
@усередині фільтра: фільтри JMESPath використовують назви полів безпосередньо, тож[?@.age > 18]неправильно; - Трактування
nullяк порожнього рядка: проєкції відкидаютьnull, але хеші з кількома виборами зберігають ключ; - Використання подвійних лапок довкола літералів в AWS CLI: оболонка спершу їх розгортає й переписує вираз.
Порада щодо налагодження: перевіряйте вираз на якомога меншому вході, перш ніж застосовувати до реальних даних, і додавайте --output json в AWS CLI, щоб бачити сирі результати. Для повсякденного редагування й форматування JSON поєднання з Редактори JSON та інструменти командного рядка робить локалізацію шляхів полів набагато швидшою.
Часті питання
Чи можна використовувати JMESPath і JSONPath разом?
Так, але зазвичай це не потрібно. Поширений підхід — обрізати маленький обʼєкт через JMESPath, потім використовувати звичайний доступ до властивості в коді. Якщо вам справді треба локалізувати за шляхом і потім змінити, JSONPath читає, а JSON Pointer пише — це ясніший поділ праці.
Чому people[*].name повертає щось інше, ніж один рядок?
Бо [*] — проєкція, і результат завжди список, навіть коли людина лише одна. Щоб отримати одне значення, додайте конвеєр та індекс: people[*].name | [0].
Що відбувається з null усередині проєкції?
Проєкція списку автоматично пропускає елементи, що обчислюються в null, тож результат може бути коротшим за оригінальний список. Хеш із кількома виборами зберігає ключ і зберігає значення null, що важливо під час побудови відповідей API.
Чи може JMESPath виконувати агрегацію й групування?
Він може виконувати базову агрегацію через sum(), max() і sort_by(). Групування, зʼєднання й віконні функції поза стандартом. Складний аналіз залишайте jq, SQL або коду.
Як вирішити між JMESPath і JSONPath?
Використовуйте JSONPath, коли треба вибрати вузли за шляхом і умовою та отримати набір вузлів; використовуйте JMESPath, коли треба переформувати результат у те, що очікує фронтенд, із перейменуванням і обрізанням полів. Коли читаєте одне відоме поле, не використовуйте жодного; просто зверніться до властивості.
Чи використовувати конвеєр, щоб узяти перше проєктоване значення?
Так. people[*].name — проєкція, тож результат завжди список; без конвеєра people[*].name[0] бере перший символ кожної назви. Пишіть people[*].name | [0], щоб отримати перше значення.