4. Поддержка функционала JSONPath

Обзор

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

JSONPath состоит из сегментов, разделенных точками. Сегмент может быть иметь форму либо простого слова, представляющего имя значения JSON, либо подстановочного символа (*), либо более замысловатой конструкции, заключённой в квадратные скобки. Точка перед сегментом скобок необязательна и может быть опущена.

Пример JSONPath Описание
$.object.name Возвращает содержимое object.name.
$.object['name'] Возвращает содержимое object.name.
$.object.['name'] Возвращает содержимое object.name.
$["object"]['name'] Возвращает содержимое object.name.
$.['object'].["name"] Возвращает содержимое object.name.
$.object.history.length() Возвращает количество элементов массива object.history.
$[?(@.name == 'Object')].price.first() Возвращает значение свойства price первого объекта по имени «Object».
$[?(@.name == 'Object')].history.first().length() Возвращает количество элементов массива history первого объекта с именем «Object».
$[?(@.price > 10)].length() Возвращает количество объектов с price больше чем 10.

Смотрите также: Экранирование специальных символов из значений LLD макросов в JSONPath.

Поддерживаемые сегменты

Сегмент Описание
<имя> Соответствие свойству объекта по имени.
* Соответствие всем свойствам объекта.
['<имя>'] Соответствие свойству объекта по имени.
['<имя>', '<имя>', ...] Соответствие свойству объекта по любому из перечисленных имён.
[<индекс>] Соответствие элементу массива по его индексу.
[<число>, <число>, ...] Соответствие элементу массива по любому из перечисленных индексов.
[*] Соответствие всем свойствам объекта или элементам массива.
[<начало>:<конец>] Соответствие элементам массива по заданному диапазону:
<начало> — первый индекс соответствия (включительно). Если не указан, соответствует всем элементам с самого начала. В случае отрицательного значения указывает начальное смещение от конца массива.
<конец> — последний индекс соответствия (не включая). Если не указан, соответствует всем элементам массива до самого конца. В случае отрицательного значения указывает начальное смещение от конца массива.
[?(<выражение>)] Соответствие объектов / элементов массива с применением выражения фильтра.

Чтобы найти соответствующий сегмент, игнорируя его происхождение (отсоединённый сегмент), его необходимо указывать с префиксом '..', например $..name или $..['name'] вернёт значения всех свойств «name».

Соответствующие имена элементов можно извлечь, добавив суффикс ~ к JSONPath. Он возвращает имя соответствующего объекта или индекс в строковом формате соответствующего элемента массива. Формат вывода следует тем же самым правилам, что и остальные запросы JSONPath — результаты определённого пути возвращаются «как есть», а результаты неопределённого пути возвращаются в виде массива. Однако, нет особого смысла извлекать имя соответствующего элемента определённого пути — оно и так уже известно.

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

Выражение фильтра — это арифметическое выражение в инфиксной нотации.

Поддерживаемые операнды:

Операнд Описание
"<text>"
'<text>'
Текстовая константа.

Пример:
'value: \'1\''
"value: '1'"
<number> Числовая константа с поддержкой научной нотации.

Пример: 123
<jsonpath starting with $> Значение, на которое указывает JSONPath от корневого узла входного документа; поддерживаются только определенные пути.

Пример: $.object.name
<jsonpath starting with @> Значение, на которое указывает JSONPath от текущего объекта/элемента; поддерживаются только определенные пути.

Пример: @.name

Поддерживаемые операторы:

Оператор Тип Описание Результат
- Binary Вычитание Number
+ Binary Сложение Number
/ Binary Деление Number
* Binary Умножение Number
== Binary Равенство Boolean (1/0)
!= Binary Неравенство Boolean (1/0)
< Binary Меньше чем Boolean (1/0)
<= Binary Меньше или равно Boolean (1/0)
> Binary Больше чем Boolean (1/0)
>= Binary Больше или равно Boolean (1/0)
=~ Binary Соответствует регулярному выражению Boolean (1/0)
! Unary Логическое НЕ Boolean (1/0)
|| Binary Логическое ИЛИ Boolean (1/0)
&& Binary Логическое И Boolean (1/0)

Функции

Функции можно использовать в конце JSONPath. Несколько функций можно включать в цепочку, если предыдущая функция возвращает значение, которое принимается следующей функцией.

Поддерживаемые функции:

Функция Описание Вход Выход
avg Среднее значение из чисел во входящем массиве Массив чисел Число
min Минимальное значение из чисел во входящем массиве Массив чисел Число
max Максимальное значение из чисел во входящем массиве Массив чисел Число
sum Сумма чисел во входящем массиве Массив чисел Число
length Количество элементов во входящем массиве Массив Число
first Первый элемент массива Массив Конструкция JSON (объект, массив, значение), в зависимости от содержимого входящего массива

Функциями агрегации JSONPath принимаются числовые значения в кавычках. Это означает, что значения преобразуются из строкового типа в числовой, если требуется агрегирование.

Несовместимые входные данные повлекут ошибку в функции.

Результирующее значение

JSONPath можно разделить на определённые и неопределённые пути. Определённый путь может вернуть только null или одно совпадение. Неопределённый путь может вернуть несколько совпадений: JSONPath с отсоединёнными несколькими именами / списком индексов, фрагментами массива или сегментами выражения. Однако, когда используется функция, JSONPath становится определённым, так как функции всегда имеют одиночное результирующее значение.

Определённый путь возвращает объект/массив/значение, на которое он ссылается. Напротив, неопределённый путь возвращает массив соответствующих объектов/массивов/значений.

Порядок свойств в результатах запроса JSONPath может не соответствовать порядку свойств исходного JSON'а из-за внутренних методов оптимизации. Например, JSONPath $.books[1]["author", "title"] может вернуть ["title", "author"]. Если сохранение исходного порядка свойств имеет важное значение, следует рассмотреть альтернативные методы обработки пост-запроса.

Правила форматирования пути

Пробельные символы (пробелы и символы табуляции) можно свободно использовать в сегментах с представлением в виде квадратных скобок и в выражениях, например: $[ 'a' ][ 0 ][ ?( $.b == 'c' ) ][ : -1 ].first( ).

Строки должны быть заключены в одинарные (') или двойные (") кавычки. Внутри строк одинарные или двойные кавычки (в зависимости от того, в какие кавычки заключена строка) и обратные косые черты (\) экранируются при помощи символа обратной косой черты (\).

Пример

{
  "books": [
    {
      "category": "reference",
      "author": "Nigel Rees",
      "title": "Изречения века",
      "price": 8.95,
      "id": 1
    },
    {
      "category": "fiction",
      "author": "Evelyn Waugh",
      "title": "Меч чести",
      "price": 12.99,
      "id": 2
    },
    {
      "category": "fiction",
      "author": "Herman Melville",
      "title": "Моби Дик",
      "isbn": "0-553-21311-3",
      "price": 8.99,
      "id": 3
    },
    {
      "category": "fiction",
      "author": "J. R. R. Tolkien",
      "title": "Властелин колец",
      "isbn": "0-395-19395-8",
      "price": 22.99,
      "id": 4
    }
  ],
  "services": {
    "delivery": {
      "servicegroup": 1000,
      "description": "Доставка на следующий день в пределах города",
      "active": true,
      "price": 5
    },
    "bookbinding": {
      "servicegroup": 1001,
      "description": "Печать и сборка книги в формате A5",
      "active": true,
      "price": 154.99
    },
    "restoration": {
      "servicegroup": 1002,
      "description": "Различные методы реставрации",
      "active": false,
      "methods": [
        {
          "description": "Химическая очистка",
          "price": 46
        },
        {
          "description": "Прессование страниц, поврежденных влагой",
          "price": 24.5
        },
        {
          "description": "Переплет порванной книги",
          "price": 99.49
        }
      ]
    }
  },
  "filters": {
    "price": 10,
    "category": "fiction",
    "no filters": "no \"filters\""
  },
  "closed message": "Магазин закрыт",
  "tags": [
    "a",
    "b",
    "c",
    "d",
    "e"
  ]
}
JSONPath Type Result
$.filters.price definite 10
$.filters.category definite fiction
$.filters['no filters'] definite no "filters"
$.filters definite {
"price": 10,
"category": "fiction",
"no filters": "no \"filters\""
}
$.books[1].title definite Меч чести
$.books[-1].author definite J. R. R. Tolkien
$.books.length() definite 4
$.tags[:] indefinite ["a", "b", "c", "d", "e" ]
$.tags[2:] indefinite ["c", "d", "e" ]
$.tags[:3] indefinite ["a", "b", "c"]
$.tags[1:4] indefinite ["b", "c", "d"]
$.tags[-2:] indefinite ["d", "e"]
$.tags[:-3] indefinite ["a", "b"]
$.tags[:-3].length() definite 2
$.books[0, 2].title indefinite ["Moby Dick", "Изречения века"]
$.books[1]['author', "title"] indefinite ["Меч чести", "Evelyn Waugh"]
$..id indefinite [1, 2, 3, 4]
$.services..price indefinite [154.99, 5, 46, 24.5, 99.49]
$.books[?(@.id == 4 - 0.4 * 5)].title indefinite ["Меч чести"]

Примечание: Этот запрос показывает, что в запросах можно использовать арифметические операции; его можно упростить до $.books[?(@.id == 2)].title
$.books[?(@.id == 2 || @.id == 4)].title indefinite ["Меч чести", "Властелин колец"]
$.books[?(!(@.id == 2))].title indefinite ["Изречения века", "Моби Дик", "Властелин колец"]
$.books[?(@.id != 2)].title indefinite ["Изречения века", "Моби Дик", "Властелин колец"]
$.books[?(@.title =~ " of ")].title indefinite ["Изречения века", "Меч чести", "Властелин колец"]
$.books[?(@.price > 12.99)].title indefinite ["Властелин колец"]
$.books[?(@.author > "Herman Melville")].title indefinite ["Изречения века", "Властелин колец"]
$.books[?(@.price > $.filters.price)].title indefinite ["Меч чести", "Властелин колец"]
$.books[?(@.category == $.filters.category)].title indefinite ["Меч чести","Моби Дик","Властелин колец"]
$.books[?(@.category == "fiction" && @.price < 10)].title indefinite ["Моби Дик"]
$..[?(@.id)] indefinite [
{
"price": 8.95,
"id": 1,
"category": "reference",
"author": "Nigel Rees",
"title": "Изречения века"
},
{
"price": 12.99,
"id": 2,
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Меч чести"
},
{
"price": 8.99,
"id": 3,
"category": "fiction",
"author": "Herman Melville",
"title": "Моби Дик",
"isbn": "0-553-21311-3"
},
{
"price": 22.99,
"id": 4,
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "Властелин колец",
"isbn": "0-395-19395-8"
}
]
$.services..[?(@.price > 50)].description indefinite ["Печать и сборка книги в формате A5", "Переплет порванной книги"]
$..id.length() definite 4
$.books[?(@.id == 2)].title.first() definite Меч чести
$..tags.first().length() definite 5

Примечание: $..tags — это неопределенный путь, поэтому он возвращает массив совпавших элементов, то есть [["a", "b", "c", "d", "e" ]]; first() возвращает первый элемент, то есть ["a", "b", "c", "d", "e"]; length() вычисляет длину элемента, то есть 5.
$.books[*].price.min() definite 8.95
$..price.max() definite 154.99
$.books[?(@.category == "fiction")].price.avg() definite 14.99
$.books[?(@.category == $.filters.xyz)].title indefinite Примечание: Запрос без совпадений возвращает NULL для определенных и неопределенных путей.
$.services[?(@.active=="true")].servicegroup indefinite [1001,1000]

Примечание: При сравнении логических значений необходимо использовать текстовые константы.
$.services[?(@.active=="false")].servicegroup indefinite [1002]

Примечание: При сравнении логических значений необходимо использовать текстовые константы.
$.services[?(@.servicegroup=="1002")]~.first() definite restoration