Что такое JSONPath?
JSONPath — это язык запросов для извлечения значений из JSON-документов. Впервые предложенный Стефаном Гёсснером в 2007 году как аналог XPath для XML, JSONPath позволяет писать выражения вида $.store.book[*].author для выборки всех полей author из массива book без циклов и ручного обхода структуры. В феврале 2024 года язык был стандартизирован организацией IETF в виде RFC 9535.
JSONPath-выражение всегда начинается с $, обозначающего корень документа. Далее цепочкой идут селекторы: точечная нотация для ключей объектов, скобочная нотация для индексов массивов, подстановочные символы для всех дочерних элементов и оператор рекурсивного спуска (..) для поиска по всем уровням вложенности. Фильтрующие выражения вида [?(@.price < 10)] позволяют выбирать элементы по условиям, проверяемым относительно их значений.
JSONPath применяется в инструментах для тестирования API, таких как Postman, платформах наблюдаемости — Datadog и Splunk, в движках рабочих процессов, например Apache NiFi, а также в библиотеках для JavaScript, Python, Go, Java и C#. Проверка выражений на реальных данных до их внедрения в код или конфигурационные файлы позволяет избежать скрытых ошибок при несоответствии структуры JSON вашим ожиданиям.
Зачем использовать этот JSONPath Tester?
Писать JSONPath-выражения вручную непросто. Пропущенная точка, неправильный тип скобки или некорректный синтаксис фильтра могут возвращать пустые результаты без какого-либо сообщения об ошибке. Этот инструмент обеспечивает мгновенную визуальную обратную связь о том, что именно соответствует вашему выражению.
Применение JSONPath
Справочник по синтаксису JSONPath
RFC 9535 определяет стандартный синтаксис JSONPath. В таблице ниже перечислены операторы, которые используются в большинстве запросов. Все выражения начинаются с $ (корневой узел) и содержат один или несколько селекторов для навигации по структуре документа.
| Оператор | Описание | Пример |
|---|---|---|
| $ | Root element | $.store |
| .key | Child property | $.store.book |
| [n] | Array index (zero-based) | $.store.book[0] |
| [*] | All elements in array/object | $.store.book[*] |
| .. | Recursive descent | $..author |
| [start:end] | Array slice | $.store.book[0:2] |
| [?()] | Filter expression | $.store.book[?(@.price<10)] |
| @ | Current node (inside filter) | $.store.book[?(@.isbn)] |
JSONPath vs jq vs XPath
JSONPath, jq и XPath решают одну и ту же задачу (запросы к структурированным данным) для разных форматов и сценариев использования. JSONPath ориентирован на JSON и доступен в виде библиотеки в большинстве языков программирования. jq — это автономный CLI-инструмент с собственным полным языком фильтрации. XPath работает с XML и является частью стека спецификаций W3C.
| Возможность | JSONPath | jq | XPath |
|---|---|---|---|
| Формат данных | JSON | JSON | XML |
| Доступ к первому элементу | $.store.book[0] | .store.book[0] | /store/book[1] |
| Рекурсивный поиск | $..price | .. | .price? | //price |
| Фильтрующее выражение | [?(@.price<10)] | select(.price < 10) | [price<10] |
| Спецификация | RFC 9535 | stedolan.github.io | W3C XPath 3.1 |
Примеры кода
Как вычислять JSONPath-выражения в популярных языках и инструментах. В каждом примере используется одна и та же JSON-структура книжного магазина для сравнения.
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
book: [
{ title: 'Moby Dick', price: 8.99 },
{ title: 'The Great Gatsby', price: 12.99 }
]
}
};
// Get all book titles
JSONPath({ path: '$.store.book[*].title', json: data });
// → ['Moby Dick', 'The Great Gatsby']
// Recursive descent — find every price in the document
JSONPath({ path: '$..price', json: data });
// → [8.99, 12.99]
// Filter — books under $10
JSONPath({ path: '$.store.book[?(@.price < 10)]', json: data });
// → [{ title: 'Moby Dick', price: 8.99 }]from jsonpath_ng.ext import parse
import json
data = {
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}
# Get all book titles
expr = parse("$.store.book[*].title")
titles = [match.value for match in expr.find(data)]
# → ['Moby Dick', 'The Great Gatsby']
# Recursive descent — all prices
expr = parse("$..price")
prices = [match.value for match in expr.find(data)]
# → [8.99, 12.99]
# Filter — books under $10
expr = parse("$.store.book[?price < 10]")
cheap = [match.value for match in expr.find(data)]
# → [{"title": "Moby Dick", "price": 8.99}]package main
import (
"fmt"
"github.com/ohler55/ojg/jp"
"github.com/ohler55/ojg/oj"
)
func main() {
src := `{
"store": {
"book": [
{"title": "Moby Dick", "price": 8.99},
{"title": "The Great Gatsby", "price": 12.99}
]
}
}`
obj, _ := oj.ParseString(src)
// Get all book titles
expr, _ := jp.ParseString("$.store.book[*].title")
results := expr.Get(obj)
fmt.Println(results)
// → [Moby Dick The Great Gatsby]
// Recursive descent — all prices
expr2, _ := jp.ParseString("$..price")
fmt.Println(expr2.Get(obj))
// → [8.99 12.99]
}# jq uses its own syntax, not JSONPath, but solves the same problem.
# Mapping common JSONPath patterns to jq:
# $.store.book[*].title → get all titles
echo '{"store":{"book":[{"title":"Moby Dick"},{"title":"Gatsby"}]}}' | \
jq '.store.book[].title'
# → "Moby Dick"
# → "Gatsby"
# $..price → recursive descent for "price" keys
echo '{"a":{"price":1},"b":{"price":2}}' | \
jq '.. | .price? // empty'
# → 1
# → 2
# Filter: books where price < 10
echo '{"store":{"book":[{"title":"A","price":8},{"title":"B","price":12}]}}' | \
jq '.store.book[] | select(.price < 10)'
# → {"title":"A","price":8}