ToolHub
View All Posts

Руководство по запросам JSONPath: как искать данные в JSON

JSON стал универсальным языком веба. API возвращают JSON, конфигурационные файлы используют JSON, базы данных хранят JSON-документы. Но по мере того, как JSON-структуры становятся больше и более вложенными, поиск конкретных значений становится всё сложнее. Здесь на помощь приходит JSONPath. JSONPath — это язык запросов, позволяющий навигировать и извлекать данные из JSON-документов с помощью лаконичных выражений пути, подобно тому, как XPath делает это для XML. Это руководство охватывает всё от базового синтаксиса до продвинутой фильтрации с практическими примерами, которые вы можете применить немедленно.

Что такое JSONPath?

JSONPath — это язык запросов JSON, первоначально предложенный Stefan Goessner в 2007 году. Он предоставляет компактный синтаксис для выбора узлов из JSON-документов, подобно тому, как CSS-селекторы нацеливаются на HTML-элементы или выражения XPath нацеливаются на XML-узлы. Вместо написания циклов и условной логики для обхода JSON-структур вы просто пишете выражение, описывающее путь к нужным данным.

Выражения JSONPath начинаются с корневого узла JSON-документа и навигируют через объекты и массивы к нужным значениям. Язык поддерживает подстановочные знаки, рекурсивный спуск, срезы массивов и выражения фильтрации, что достаточно для большинства задач извлечения данных.

Пример JSON-документа

В этом руководстве мы будем использовать следующий JSON-документ в качестве рабочего примера. Это классический пример из оригинального предложения JSONPath с дополнительными данными:

{
  "store": {
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J.R.R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

Справочник по синтаксису JSONPath

JSONPath использует небольшой набор операторов, которые комбинируются для формирования мощных запросов. Вот полный справочник синтаксиса:

ОператорОписаниеПример
$Корневой узел документа$
.Оператор потомка (доступ к свойствам объекта)$.store
[]Оператор индекса массива или потомка$.store.book[0]
[*]Подстановочный знак для всех элементов массива$.store.book[*]
..Рекурсивный спуск (поиск на всех уровнях)$..author
.keyИменованное свойство объекта$.store.bicycle
['key']Скобочная нотация для доступа к свойствам$['store']['book']
[start:end]Срез массива (конечный индекс не включается)$.store.book[0:2]
[?()]Выражение фильтрации< 10)]
()Скриптовое выражение (зависит от реализации)$.store.book[(@.length-1)]

Базовые выражения JSONPath

Доступ к корневому узлу и прямым потомкам

Знак доллара $ представляет корневой узел JSON-документа. Оттуда вы используете точечную нотацию для доступа к свойствам объекта и скобочную нотацию для доступа к индексам массива.

ВыражениеРезультат
$Весь JSON-документ
$.storeОбъект store (содержит массив book и объект bicycle)
$.store.bicycle{"color": "red", "price": 19.95}
$.store.bicycle.color"red"
$.store.bookВесь массив book
$.store.book[0]Первый объект книги
$.store.book[0].title"Sayings of the Century"
$.store.book[3].author"J.R.R. Tolkien"

Скобочная нотация

Скобочная нотация является альтернативой точечной нотации и полезна, когда имена свойств содержат специальные символы, пробелы или начинаются с цифры:

$.store['book'][0]['title']
$['store']['bicycle']['color']

Скобочная и точечная нотации взаимозаменяемы для свойств объектов. Однако скобочная нотация обязательна, когда имя свойства динамическое или содержит символы, недопустимые в точечной нотации.

Оператор подстановки

Оператор подстановки * соответствует всем элементам массива или всем свойствам объекта. Он чрезвычайно полезен для извлечения всех значений на определённом уровне без знания конкретных ключей или индексов.

ВыражениеРезультат
$.store.*Все значения в объекте store (массив book и объект bicycle)
$.store.book[*]Все книги в массиве (все четыре объекта book)
$.store.book[*].authorВсе имена авторов: ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J.R.R. Tolkien"]
$.store.*.priceЦена bicycle и массив book (не цены отдельных книг)

Рекурсивный спуск: оператор двойной точки

Оператор двойной точки .. — одна из самых мощных возможностей JSONPath. Он ищет указанный ключ на каждом уровне JSON-дерева, а не только среди прямых потомков. Думайте о нём как о глубоком поиске по всему документу.

ВыражениеРезультат
$..authorЗначения author во всех местах документа
$..priceВсе значения price: [8.95, 12.99, 8.99, 22.99, 19.95]
$..book[2]Третья книга, найденная рекурсивно
$..categoryВсе значения category: ["reference", "fiction", "fiction", "fiction"]

Оператор рекурсивного спуска особенно полезен, когда вы не знаете точный путь к нужным данным или когда один и тот же ключ появляется на нескольких уровнях вложенности. Например, если ответ API содержит поля id на разных уровнях вложенности, $..id соберёт все id.

Срезы массивов

Срезы массивов позволяют выбрать диапазон элементов из массива. Синтаксис [start:end] выбирает элементы от начального индекса до (но не включая) конечного индекса. Поддерживаются как положительные, так и отрицательные индексы.

ВыражениеРезультат
$.store.book[0:2]Первые две книги (индексы 0 и 1)
$.store.book[1:3]Вторая и третья книги (индексы 1 и 2)
$.store.book[-1]Последняя книга (The Lord of the Rings)
$.store.book[-2:]Последние две книги
$.store.book[:2]Первые две книги (то же, что [0:2])
$.store.book[2:]Третья книга и далее

Обратите внимание, что поведение срезов может немного отличаться в разных реализациях JSONPath. Приведённый выше синтаксис следует соглашениям, используемым большинством популярных библиотек и стандартом IETF RFC 9535.

Выражения фильтрации

Выражения фильтрации — это то, где JSONPath становится по-настоящему мощным. Они позволяют выбирать элементы на основе условий, а не позиции. Синтаксис фильтрации использует [?(condition)], где условие оценивается для каждого элемента.

Операторы сравнения

JSONPath поддерживает следующие операторы сравнения в выражениях фильтрации:

ОператорЗначениеПример
==Равно[?(@.category == "fiction")]
!=Не равно[?(@.category != "fiction")]
<Меньше< 10)]
<=Меньше или равно<= 8.99)]
>Больше[?(@.price > 15)]
>=Больше или равно[?(@.price >= 12.99)]
=~Соответствие регулярному выражению (частичная поддержка)[?(@.author =~ /Tolkien/i)]

Практические примеры фильтрации

Используя наш пример документа, вот практические выражения фильтрации и их результаты:

Find all books cheaper than $10:

< 10)]

Возвращает книги "Sayings of the Century" ($8.95) и "Moby Dick" ($8.99).

Find all fiction books:

$.store.book[?(@.category == "fiction")]

Возвращает три книги: "Sword of Honour", "Moby Dick" и "The Lord of the Rings".

Find books with an ISBN:

$.store.book[?(@.isbn)]

Возвращает книги, имеющие свойство isbn: "Moby Dick" и "The Lord of the Rings". Это работает, потому что фильтр проверяет существование свойства.

Find the most expensive book:

$.store.book[?(@.price > 20)]

Возвращает "The Lord of the Rings" ($22.99).

Логические операторы в фильтрах

Вы можете комбинировать условия с помощью логических операторов:

< 10 && @.category == "fiction")]
$.store.book[?(@.price > 15 || @.category == "reference")]

Первое выражение находит художественные книги дешевле $10 (только "Moby Dick"). Второе находит книги дороже $15 или в категории reference (возвращает "Sayings of the Century" и "The Lord of the Rings").

JSONPath и XPath

JSONPath был явно смоделирован по образцу XPath, и эти два языка имеют много концептуальных сходств. Понимание этой связи полезно, если вы знакомы с обработкой XML.

ФункцияJSONPathXPath
Корневой символ$/
Доступ к потомкам.key или ['key']/element
Индекс массива[0][1] (начинается с 1)
Подстановочный знак**
Рекурсивный спуск..//
Фильтрация[?(condition)][condition]
АтрибутН/Д (JSON не имеет атрибутов)@attr
Текущий узел@ (в фильтрах). или current()
РодительНе поддерживается..
ОсиНе поддерживаются13 осей (ancestor, following и т.д.)
Модель данныхОбъекты и массивыЭлементы, атрибуты, текстовые узлы

Ключевое отличие в том, что XPath работает с богатой древовидной моделью, имеющей элементы, атрибуты, текстовые узлы, пространства имён и инструкции обработки. JSONPath работает с более простой моделью объектов (отображений ключ-значение) и массивов (упорядоченных списков). Эта простота делает JSONPath легче в изучении, но менее выразительным, чем XPath для сложных запросов.

JSONPath на практике

Тестирование API

JSONPath незаменим для тестирования API. Когда вы отправляете запрос к API и получаете большой JSON-ответ, JSONPath позволяет вам утверждать конкретные значения без ручной навигации по всей структуре. Большинство инструментов тестирования API нативно поддерживают JSONPath.

Например, в тесте вы можете проверить, что цена первой книги меньше $10:

// Using a JSONPath assertion in testing
response.jsonPath().get("store.book[0].price").should(equals(8.95));

// Find all books by a specific author
response.jsonPath().get("store.book[?(@.author == 'Herman Melville')].title");
// Returns: ["Moby Dick"]

Трансформация данных

При интеграции систем, использующих разные форматы данных, JSONPath помогает извлекать и преобразовывать конкретные поля. Вы можете извлечь значения из одной JSON-структуры и сопоставить их с другой без написания сложного кода обхода.

Управление конфигурацией

Сложные конфигурационные файлы часто содержат глубоко вложенный JSON. JSONPath позволяет запрашивать конкретные значения конфигурации без загрузки и парсинга всей структуры. Инструменты, такие как jq, используют синтаксис, подобный JSONPath, для обработки JSON в конвейерах командной строки.

Мониторинг и оповещения

В системах наблюдаемости запросы JSONPath могут извлекать метрики из JSON-форматированных логов и ответов API. Вы можете настроить оповещения, которые срабатывают, когда запрос JSONPath возвращает значение, превышающее порог.

Реализации JSONPath

JSONPath доступен практически на всех языках программирования. Вот самые популярные библиотеки:

ЯзыкБиблиотекаУстановка
JavaScriptjsonpath-plusnpm install jsonpath-plus
Pythonjsonpath-ngpip install jsonpath-ng
JavaJsonPath (Jayway)Maven: com.jayway.jsonpath
C#Json.NET (Newtonsoft)NuGet: Newtonsoft.Json
Gogjsongo get github.com/tidwall/gjson
PHPjsonpathcomposer require softcreatr/jsonpath
Rubyjsonpathgem install jsonpath

Пример на JavaScript

< 10)]',
  json: data
});
// Returns books with price < 10

Python Example

from jsonpath_ng import parse

data = { /* our sample JSON */ }

# Find all authors
author_expr = parse('$..author')
authors = [match.value for match in author_expr.find(data)]
# ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J.R.R. Tolkien"]

# Find cheap books
cheap_expr = parse('$.store.book[?(@.price < 10)]')
cheap_books = [match.value for match in cheap_expr.find(data)]

Common Pitfalls and Tips

Совет: при работе со сложными выражениями JSONPath стройте их постепенно. Начните с простого пути, такого как $.store, убедитесь, что он работает, затем постепенно расширяйте: $.store.book, затем $.store.book[*], затем $.store.book[*].price, и наконец добавьте фильтры. Это значительно упрощает отладку.

Распространённые pitfalls и советы

В 2024 году IETF опубликовал RFC 9535, формально стандартизировав JSONPath. Спецификация устраняет многие неоднозначности и несоответствия, существовавшие между реализациями. Ключевые аспекты стандарта включают:

Если вы начинаете новый проект, отдавайте предпочтение библиотекам, реализующим RFC 9535, для максимальной совместимости и предсказуемого поведения.

Нужно быстро выполнить запрос к JSON-данным? Попробуйте наш бесплатный онлайн-тестер JSONPath, который оценивает выражения в реальном времени на вашем JSON-документе.

Попробовать JSONPath Finder

Стандарт IETF: RFC 9535

Пример на Python

JSONPath — это язык запросов JSON, подобно тому, как XPath для XML. Он использует выражения пути для навигации и извлечения конкретных значений из JSON-документов. Выражение JSONPath, такое как $.store.book[0].title, позволяет точно находить данные в сложных вложенных JSON-структурах без написания пользовательского кода парсинга.

Что такое JSONPath?

JSONPath разработан для объектно-массивной структуры JSON, в то время как XPath разработан для древовидной структуры элементов и атрибутов XML. JSONPath использует $ как корневой узел, точечную нотацию для доступа к объектам и скобочную нотацию для доступа к массивам. XPath использует / для разделения пути и @ для доступа к атрибутам. JSONPath проще, но менее функционально богат, чем XPath.

Чем JSONPath отличается от XPath?

Двойная точка (..) в JSONPath — это оператор рекурсивного спуска. Он ищет именованный ключ на всех уровнях JSON-структуры, а не только среди прямых потомков. Например, $..author находит все ключи author в любом месте документа, независимо от глубины вложенности.

Что означает двойная точка (..) в JSONPath?

<, ><=, >< 10)] находит все книги дешевле $10.

Может ли JSONPath фильтровать данные по условию?

JSONPath был первоначально предложен Stefan Goessner в 2007 году без формальной спецификации, что привело к различиям между реализациями. В 2024 году IETF опубликовал RFC 9535, формально стандартизировав JSONPath. Современные реализации сходятся к этому стандарту, но некоторые старые библиотеки могут иметь небольшие синтаксические различия.