JSONPath Tutorial: Query JSON Like a Pro
Master JSONPath syntax with practical examples. Learn every operator, filter expression, and real-world query pattern to extract data from complex JSON structures.
Что такое JSONPath?
JSONPath — это язык запросов для JSON, аналогичный тому, что XPath делает для XML. Он позволяет вам заглянуть в сложную структуру JSON и извлечь именно те данные, которые вам нужны, используя лаконичный синтаксис выражений. Вместо того чтобы писать циклы и условные операторы для поиска по вложенным объектам и массивам, вы пишете одно выражение пути, например $.store.book[?(@.price < 10)].title, и получаете все названия книг стоимостью менее десяти долларов.
Стефан Гесснер представил JSONPath в 2007 году, и с тех пор он стал де-факто стандартом среди языков и инструментов. Если вы работаете с REST API, конфигурационными файлами или любыми нетривиальными данными JSON, JSONPath сэкономит вам значительное время.
JSONPath vs. XPath: Быстрое Сравнение
Если вы уже знаете XPath, JSONPath будет вам знаком. Ключевое отличие заключается в том, что JSON имеет более простую модель данных — объекты и массивы вместо элементов, атрибутов и пространств имен. Вот как они сравниваются:
| Концепция | XPath | JSONPath |
|---|---|---|
| Корень | / | $ |
| Ребенок | /child | .child или ['child'] |
| Рекурсивный спуск | // | .. |
| Шаблон | * | * |
| Индекс массива | [1] (с 1) | [0] (с 0) |
| Фильтр | [predicate] | [?(expression)] |
Полная Справка по Операторам JSONPath
Каждое выражение JSONPath начинается с корня $ и соединяет операторы для более глубокого навигации. Вот все операторы, которые вам нужно знать:
| Оператор | Описание | Пример |
|---|---|---|
$ | Корневой объект или массив | $ — весь документ |
.key | Ребенок по имени (точечная нотация) | $.store.name |
['key'] | Ребенок по имени (скобочная нотация) | $['store']['name'] |
.. | Рекурсивный спуск — ищет всех потомков | $..price — каждое поле "price" |
* | Шаблон — все члены объекта или массива | $.store.* |
[n] | Индекс массива (с 0) | $.items[0] |
[start:end] | Срез массива (конечный индекс не включен) | $.items[0:3] — первые три элемента |
[start:end:step] | Срез массива с шагом | $.items[::2] — каждый второй элемент |
[n,m] | Несколько индексов массива | $.items[0,2,4] |
[?()] | Выражение фильтра | $.items[?(@.price > 10)] |
@ | Текущий узел (используется внутри фильтров) | @.name == 'Alice' |
Пример JSON для Наших Примеров
Мы будем использовать этот JSON-документ на протяжении всего учебника. Он представляет собой ответ API небольшого книжного магазина:
{
"store": {
"name": "TechBooks Online",
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95,
"inStock": true
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99,
"inStock": false
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99,
"inStock": true
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99,
"inStock": true
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
Практические Примеры JSONPath
1. Получить Название Магазина
$.store.name
Результат:
"TechBooks Online"
2. Получить Все Названия Книг
$.store.book[*].title
Результат:
[
"Sayings of the Century",
"Sword of Honour",
"Moby Dick",
"The Lord of the Rings"
]
3. Получить Первую Книгу
$.store.book[0]
Результат: весь объект первой книги.
4. Получить Последнюю Книгу
$.store.book[-1]
Отрицательные индексы считаются с конца. -1 дает вам последний элемент.
5. Срез: Первые Две Книги
$.store.book[0:2]
Возвращает книги с индексами 0 и 1. Конечный индекс не включен, как в slice() Python и JavaScript.
6. Получить Все Цены во Всём Документе
$..price
Результат:
[8.95, 12.99, 8.99, 22.99, 19.95]
Оператор рекурсивного спуска .. находит каждое поле price независимо от глубины — включая цену велосипеда.
7. Фильтр: Книги Стоимостью Менее $10
$.store.book[?(@.price < 10)]
Возвращает две книги, стоимостью 8.95 и 8.99. Символ @ ссылается на текущий элемент, который оценивается.
8. Фильтр: Книги, У Которых Есть ISBN
$.store.book[?(@.isbn)]
Возвращает только "Моби Дика" и "Властелина колец".
9. Фильтр: Художественные Книги В Наличии
$.store.book[?(@.category == 'fiction' && @.inStock == true)]
Вы можете комбинировать условия с помощью && (и) и || (или).
10. Несколько Индексов
$.store.book[0,3]
Выбирайте конкретные элементы. Возвращает первую и четвертую книги.
11. Шаблон на Объекте
$.store.bicycle.*
Результат:
["red", 19.95]
Возвращает все значения объекта велосипеда.
12. Получить Авторов Дорогих Книг
$.store.book[?(@.price > 15)].author
Результат:
["J. R. R. Tolkien"]
Попробуйте сами: Вставьте приведенный выше пример JSON и поэкспериментируйте с этими выражениями в нашем JSONPath Tester.
Выражения Фильтра В Глубину
Выражения фильтра — это самая мощная часть JSONPath. Они находятся внутри [?()] и оценивают булевое условие для каждого элемента. Вот операторы, которые вы можете использовать:
| Оператор | Значение | Пример |
|---|---|---|
== | Равно | @.status == 'active' |
!= | Не равно | @.role != 'admin' |
> | Больше | @.age > 18 |
>= | Больше или равно | @.score >= 90 |
< | Меньше | @.price < 50 |
<= | Меньше или равно | @.quantity <= 0 |
=~ | Соответствие регулярному выражению (некоторые реализации) | @.name =~ /^J.*/ |
&& | Логическое И | @.price > 5 && @.price < 20 |
| ` | ` |
Один распространенный шаблон — это проверка существования свойства. Запись @.isbn без оператора сравнения возвращает true, если свойство существует и не равно null.
Реальные Сценарии
Извлечение Всех Электронных Почт из Ответа API
Предположим, вы получили ответ от API управления пользователями с глубоко вложенными контактными объектами:
{
"users": [
{
"name": "Alice",
"contacts": { "email": "alice@example.com", "phone": "555-0101" }
},
{
"name": "Bob",
"contacts": { "email": "bob@example.com", "phone": "555-0102" }
}
]
}
Запрос: $..email
Результат:
["alice@example.com", "bob@example.com"]
Оператор рекурсивного спуска находит каждое поле email, независимо от того, где оно находится в иерархии.
Найти Продукты по Ценовому Диапазону
$.products[?(@.price >= 25 && @.price <= 100)]
Полезно при фильтрации ответов API на стороне клиента перед отображением списка продуктов.
Получить Вложенные Значения Конфигурации
$.config.database.connections[0].host
Погружайтесь в конфигурационные файлы, не пиша цепочку опционального доступа, как config?.database?.connections?.[0]?.host.
JSONPath в Разных Языках
JavaScript / Node.js
Пакет jsonpath-plus является самой популярной реализацией:
import { JSONPath } from "jsonpath-plus";
const data = { store: { book: [{ title: "Moby Dick", price: 8.99 }] } };
const titles = JSONPath({ path: "$.store.book[*].title", json: data });
console.log(titles); // ["Moby Dick"]
Python
Используйте jsonpath-ng для стандартной реализации:
from jsonpath_ng.ext import parse
data = {"store": {"book": [{"title": "Moby Dick", "price": 8.99}]}}
expr = parse("$.store.book[*].title")
matches = [match.value for match in expr.find(data)]
print(matches) # ['Moby Dick']
Java
Библиотека Jayway JSONPath — это лучший выбор для проектов на JVM:
import com.jayway.jsonpath.JsonPath;
String json = "{\"store\":{\"book\":[{\"title\":\"Moby Dick\",\"price\":8.99}]}}";
List<String> titles = JsonPath.read(json, "$.store.book[*].title");
System.out.println(titles); // [Moby Dick]
Подводные Камни и Ограничения
JSONPath мощен, но есть несколько моментов, на которые стоит обратить внимание, прежде чем полагаться на него в производстве:
- Нет стандартной спецификации (до недавнего времени). JSONPath был неформально определен, и разные библиотеки реализовали его с тонкими вариациями. IETF опубликовал RFC 9535 в 2024 году для стандартизации синтаксиса, но многие библиотеки все еще следуют оригинальной спецификации Гесснера. Всегда проверяйте документацию вашей библиотеки на поддерживаемые функции.
- Поддержка регулярных выражений варьируется. Оператор
=~для регулярных выражений не поддерживается повсеместно. Библиотека Jayway для Java поддерживает его; многие библиотеки JavaScript — нет. - Нет операций записи. JSONPath является только для чтения. Вы не можете использовать его для изменения оригинального документа. Для мутаций вам нужен JSON Patch (RFC 6902) или ручное прохождение.
- Производительность на огромных документах. Рекурсивный спуск (
..) сканирует каждый узел в дереве. В документах с миллионами узлов это может быть медленно. Предпочитайте конкретные пути, когда важна производительность. - Несоответствие типов возвращаемых значений. Некоторые библиотеки возвращают одно значение для одиночных совпадений и массив для множественных совпадений. Другие всегда возвращают массив. Нормализуйте тип возвращаемого значения в вашем коде, чтобы избежать сюрпризов.
- Специальные символы в ключах. Если ваши ключи JSON содержат точки, пробелы или другие специальные символы, используйте скобочную нотацию:
$('my.key')вместо$.my.key.
JSONPath vs. jq
Вы можете задаться вопросом, как JSONPath сравнивается с jq, популярным командным процессором JSON. Краткий ответ: они решают схожие проблемы, но для разных аудиторий.
- JSONPath предназначен для встраивания в код приложений. Он имеет поддержку библиотек на каждом основном языке и простой синтаксис запросов.
- jq — это полноценный язык трансформации данных. Он может фильтровать, отображать, сокращать и изменять JSON. Он более мощный, но имеет более крутой кривую обучения и в основном используется в командной строке.
Для выполнения запросов данных внутри приложения JSONPath является прагматичным выбором. Для сценариев оболочки и конвейеров данных jq трудно превзойти.
Быстрая Справка
| Что Вы Хотите | JSONPath |
|---|---|
| Весь документ | $ |
| Конкретное свойство | $.user.name |
| Все элементы в массиве | $.items[*] |
| Первый элемент | $.items[0] |
| Последний элемент | $.items[-1] |
| Диапазон элементов | $.items[1:4] |
| Каждый n-й элемент | $.items[::2] |
| Все поля "name" в любом месте | $..name |
| Фильтр по значению | $.items[?(@.price < 10)] |
| Фильтр по существованию | $.items[?(@.discount)] |
| Несколько условий | $.items[?(@.qty > 0 && @.active)] |
Попробуйте сами: Откройте JSONPath Tester, чтобы выполнять запросы к вашим собственным данным JSON в реальном времени.