JSON Toolkit

JMESPath для початківців: проєкційні запити, що кращі за JSONPath

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

Початковий~9 хв читанняОпубліковано 2026-10-31Оновлено 2026-10-31
Обкладинка статті «JMESPath для початківців»

Коротко: використовуйте 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], щоб отримати перше значення.