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 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], чтобы получить первое значение.