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 Specification |
Руководство для решения в одном предложении:
- Используйте 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], чтобы получить первое значение.