← Вернуться к блогу

JSONPath: Поиск путей и навигация по JSON данным

Полное руководство по JSONPath - мощному языку запросов для поиска и извлечения данных из JSON структур.

Big JSON Team13 мин чтенияadvanced
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

13 мин чтения

# JSONPath: Поиск путей и навигация по JSON данным

JSONPath - это мощный язык запросов для JSON, подобный XPath для XML. Он позволяет извлекать и манипулировать данными в сложных JSON структурах. В этом подробном руководстве мы изучим все возможности JSONPath.

Что такое JSONPath?

JSONPath - это язык запросов, который позволяет выбирать и извлекать данные из JSON документов. Он был создан Стефаном Гёсснером как альтернатива XPath для JSON.

Базовый синтаксис

$ - корневой элемент

. - текущий элемент

.. - рекурсивный поиск

- wildcard (любой элемент)

[] - индекс массива или фильтр

Примеры данных для работы

Будем использовать следующий JSON для примеров:

{

"магазин": {

"название": "Книжный мир",

"книги": [

{

"категория": "художественная",

"автор": "Лев Толстой",

"название": "Война и мир",

"isbn": "978-5-17-098352-4",

"цена": 500,

"наличие": true

},

{

"категория": "художественная",

"автор": "Федор Достоевский",

"название": "Преступление и наказание",

"isbn": "978-5-17-098353-1",

"цена": 450,

"наличие": true

},

{

"категория": "техническая",

"автор": "Брюс Эккель",

"название": "Философия Java",

"isbn": "978-5-496-00739-5",

"цена": 800,

"наличие": false

},

{

"категория": "техническая",

"автор": "Роберт Мартин",

"название": "Чистый код",

"isbn": "978-5-496-00487-5",

"цена": 650,

"наличие": true

}

],

"велосипеды": [

{

"тип": "горный",

"производитель": "Giant",

"модель": "Talon 1",

"цена": 45000,

"цвет": "черный"

},

{

"тип": "шоссейный",

"производитель": "Trek",

"модель": "Domane AL 2",

"цена": 65000,

"цвет": "красный"

}

]

}

}

Базовые операции

Получение корневого элемента

// JSONPath запрос

$

// Результат: весь JSON документ

Доступ к свойству

// Название магазина

$.магазин.название

// Результат

"Книжный мир"

Доступ к массиву

// Все книги

$.магазин.книги

// Результат: массив всех книг

[{...}, {...}, {...}, {...}]

Доступ к элементу массива по индексу

// Первая книга

$.магазин.книги[0]

// Результат

{

"категория": "художественная",

"автор": "Лев Толстой",

"название": "Война и мир",

"isbn": "978-5-17-098352-4",

"цена": 500,

"наличие": true

}

// Последняя книга

$.магазин.книги[-1]

// Вторая и третья книги

$.магазин.книги[1:3]

Wildcard (Подстановочные знаки)

Все элементы на уровне

// Все свойства магазина

$.магазин.

// Результат: массив всех значений

["Книжный мир", [...книги...], [...велосипеды...]]

Все элементы в массиве

// Все книги

$.магазин.книги[]

// Названия всех книг

$.магазин.книги[].название

// Результат

[

"Война и мир",

"Преступление и наказание",

"Философия Java",

"Чистый код"

]

Рекурсивный поиск

Поиск по всему документу

// Все цены в документе

$..цена

// Результат

[500, 450, 800, 650, 45000, 65000]

// Все названия

$..название

// Результат

[

"Книжный мир",

"Война и мир",

"Преступление и наказание",

"Философия Java",

"Чистый код"

]

Фильтры

Синтаксис фильтров

Фильтры используют ?(выражение) синтаксис.

Сравнение значений

// Книги дороже 500 рублей

$.магазин.книги[?(@.цена > 500)]

// Результат

[

{

"категория": "техническая",

"автор": "Брюс Эккель",

"название": "Философия Java",

"цена": 800,

"наличие": false

},

{

"категория": "техническая",

"автор": "Роберт Мартин",

"название": "Чистый код",

"цена": 650,

"наличие": true

}

]

Проверка на равенство

// Технические книги

$.магазин.книги[?(@.категория == 'техническая')]

// Книги в наличии

$.магазин.книги[?(@.наличие == true)]

// Книги не в наличии

$.магазин.книги[?(@.наличие == false)]

Проверка существования свойства

// Элементы с ISBN

$.магазин.книги[?(@.isbn)]

// Элементы без ISBN

$.магазин.велосипеды[?(!@.isbn)]

Логические операторы

// И (AND)

$.магазин.книги[?(@.цена > 400 && @.наличие == true)]

// ИЛИ (OR)

$.магазин.книги[?(@.цена < 500 || @.категория == 'техническая')]

// НЕ (NOT)

$.магазин.книги[?(!(@.цена > 700))]

Регулярные выражения

// Книги, название которых содержит "код"

$.магазин.книги[?(@.название =~ /код/i)]

// Книги авторов, чья фамилия начинается с "Т"

$.магазин.книги[?(@.автор =~ /^Лев/)]

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

Базовые срезы

// Первые две книги

$.магазин.книги[0:2]

// С третьей до конца

$.магазин.книги[2:]

// Последние две

$.магазин.книги[-2:]

// Все кроме первой и последней

$.магазин.книги[1:-1]

Срезы с шагом

// Каждая вторая книга

$.магазин.книги[::2]

// Результат: 1-я и 3-я книга

[

{"название": "Война и мир", ...},

{"название": "Философия Java", ...}

]

Сложные запросы

Множественные условия

// Доступные технические книги дороже 600

$.магазин.книги[?(@.категория == 'техническая' && @.наличие == true && @.цена > 600)]

// Результат

[

{

"категория": "техническая",

"автор": "Роберт Мартин",

"название": "Чистый код",

"цена": 650,

"наличие": true

}

]

Вложенные запросы

// Сначала фильтруем, потом выбираем поля

$.магазин.книги[?(@.цена < 600)].название

// Результат

[

"Война и мир",

"Преступление и наказание"

]

Использование в коде

JavaScript (с библиотекой JSONPath)

const jsonpath = require('jsonpath');

const данные = {

"магазин": {

"книги": [

{"название": "Война и мир", "цена": 500, "наличие": true},

{"название": "Чистый код", "цена": 650, "наличие": true}

]

}

};

// Простой запрос

const названия = jsonpath.query(данные, '$.магазин.книги[].название');

console.log(названия);

// ["Война и мир", "Чистый код"]

// С фильтром

const дорогиеКниги = jsonpath.query(

данные,

'$.магазин.книги[?(@.цена > 500)]'

);

console.log(дорогиеКниги);

// [{"название": "Чистый код", "цена": 650, "наличие": true}]

// Получение путей

const пути = jsonpath.paths(данные, '$.магазин.книги[].название');

console.log(пути);

// [

// ["$", "магазин", "книги", 0, "название"],

// ["$", "магазин", "книги", 1, "название"]

// ]

// Установка значений

jsonpath.value(данные, '$.магазин.книги[0].цена', 550);

console.log(данные.магазин.книги[0].цена); // 550

Python (с библиотекой jsonpath-ng)

from jsonpath_ng import jsonpath, parse

данные = {

"магазин": {

"книги": [

{"название": "Война и мир", "цена": 500, "наличие": True},

{"название": "Чистый код", "цена": 650, "наличие": True}

]

}

}

# Простой запрос

путь = parse('$.магазин.книги[].название')

совпадения = [match.value for match in путь.find(данные)]

print(совпадения)

# ["Война и мир", "Чистый код"]

# С использованием jsonpath-rw для фильтров

from jsonpath_rw_ext import parse as parse_ext

# Фильтр

путь = parse_ext('$.магазин.книги[?(@.цена > 500)]')

результат = путь.find(данные)

for совпадение in результат:

print(совпадение.value)

PHP

<?php

require 'vendor/autoload.php';

use FlowJSONPathJSONPath;

$данные = json_decode('{

"магазин": {

"книги": [

{"название": "Война и мир", "цена": 500},

{"название": "Чистый код", "цена": 650}

]

}

}');

$jsonPath = new JSONPath($данные);

// Простой запрос

$результат = $jsonPath->find('$.магазин.книги[].название');

print_r($результат->getData());

// Array ( [0] => Война и мир [1] => Чистый код )

// С фильтром

$дорогие = $jsonPath->find('$.магазин.книги[?(@.цена > 500)]');

print_r($дорогие->getData());

?>

Продвинутые техники

Агрегация данных

// Сумма всех цен книг

const цены = jsonpath.query(данные, '$.магазин.книги[].цена');

const сумма = цены.reduce((acc, цена) => acc + цена, 0);

console.log('Общая стоимость:', сумма);

// Средняя цена

const средняя = сумма / цены.length;

console.log('Средняя цена:', средняя);

Группировка

// Группировка книг по категории

const книги = jsonpath.query(данные, '$.магазин.книги[]');

const поКатегориям = книги.reduce((acc, книга) => {

const категория = книга.категория;

if (!acc[категория]) {

acc[категория] = [];

}

acc[категория].push(книга);

return acc;

}, {});

console.log(поКатегориям);

// {

// "художественная": [...],

// "техническая": [...]

// }

Преобразование данных

// Создание нового объекта с преобразованными данными

const книгиВНаличии = jsonpath.query(

данные,

'$.магазин.книги[?(@.наличие == true)]'

).map(книга => ({

название: книга.название,

автор: книга.автор,

ценаСоСкидкой: книга.цена 0.9

}));

console.log(книгиВНаличии);

Сравнение с другими подходами

JSONPath vs Прямой доступ

// Прямой доступ

const название1 = данные.магазин.книги[0].название;

// JSONPath

const название2 = jsonpath.query(данные, '$.магазин.книги[0].название')[0];

// JSONPath более гибкий для динамических запросов

const путь = пользовательскийВвод; // например, '$.магазин.книги[0].название'

const результат = jsonpath.query(данные, путь);

JSONPath vs jq

# jq (командная строка)

cat данные.json | jq '.магазин.книги[] | select(.цена > 500)'

# Эквивалентный JSONPath

$.магазин.книги[?(@.цена > 500)]

Онлайн инструменты для JSONPath

JSONPath Online Evaluator

URL: jsonpath.com

Возможности:

  • Интерактивное тестирование
  • Подсветка результатов
  • Примеры запросов
  • Документация

Использование:
1. Вставить JSON в левую панель
  • Ввести JSONPath выражение
  • Увидеть результаты справа
  • Экспериментировать с запросами
  • JSONPath Finder

    Встроенный в JSON Editor Online - визуальный способ построения JSONPath запросов.

    Практические примеры

    Пример 1: Поиск пользователей

    {
    

    "пользователи": [

    {

    "id": 1,

    "имя": "Иван",

    "возраст": 30,

    "город": "Москва",

    "роли": ["admin", "user"]

    },

    {

    "id": 2,

    "имя": "Мария",

    "возраст": 25,

    "город": "СПб",

    "роли": ["user"]

    },

    {

    "id": 3,

    "имя": "Петр",

    "возраст": 35,

    "город": "Москва",

    "роли": ["moderator", "user"]

    }

    ]

    }

    Запросы:

    // Все имена
    

    $.пользователи[].имя

    // Пользователи старше 25

    $.пользователи[?(@.возраст > 25)]

    // Пользователи из Москвы

    $.пользователи[?(@.город == 'Москва')]

    // Администраторы

    $.пользователи[?(@.роли[] == 'admin')]

    Пример 2: Анализ заказов

    {
    

    "заказы": [

    {

    "id": "ORD001",

    "клиент": "Иван",

    "товары": [

    {"название": "Книга", "цена": 500, "количество": 2},

    {"название": "Ручка", "цена": 50, "количество": 5}

    ],

    "статус": "доставлен"

    },

    {

    "id": "ORD002",

    "клиент": "Мария",

    "товары": [

    {"название": "Ноутбук", "цена": 75000, "количество": 1}

    ],

    "статус": "обработка"

    }

    ]

    }

    Запросы:

    // Все товары во всех заказах
    

    $..товары[]

    // Доставленные заказы

    $.заказы[?(@.статус == 'доставлен')]

    // Заказы с товарами дороже 1000

    $.заказы[?(@.товары[].цена > 1000)]

    // Все цены товаров

    $..товары[].цена

    Оптимизация запросов

    Специфичность запросов

    Эффективно:

    $.магазин.книги[*].название

    Неэффективно:

    $..название  // Ищет по всему документу

    Кэширование результатов

    // Плохо - повторный парсинг
    

    for (let i = 0; i < 1000; i++) {

    const результат = jsonpath.query(большиеДанные, сложныйЗапрос);

    }

    // Хорошо - один раз запрос

    const результат = jsonpath.query(большиеДанные, сложныйЗапрос);

    for (let i = 0; i < 1000; i++) {

    // Использовать результат

    }

    Ограничения JSONPath

  • Нет модификации: JSONPath только читает данные
  • Производительность: Рекурсивный поиск может быть медленным
  • Сложные вычисления: Ограниченные возможности для комплексных операций
  • Заключение

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

    Ключевые выводы:

    • JSONPath похож на XPath для XML
    • Поддерживает фильтры, wildcard и рекурсивный поиск
    • Доступен для JavaScript, Python, PHP и других языков
    • Идеален для динамических запросов к JSON
    • Онлайн инструменты помогают в обучении и отладке

    Освоив JSONPath, вы сможете эффективно работать с любыми JSON данными, независимо от их сложности!

    Share:

    Похожие статьи

    Read in English