JSON-Diff

Сравните два JSON-документа и увидите, какие именно поля добавлены, удалены или изменены

JSON A (слева / до)

JSON B (справа / после)

JSON-Diff

Сравните два JSON-документа и увидите, какие именно поля добавлены, удалены или изменены

Возможности

  • Структурный diff между двумя JSON-значениями — объекты, массивы и примитивы корректно обходятся (null трактуется как скаляр, а не объект; баг typeof null исправлен)
  • Каждое различие сообщается как путь RFC 6901 JSON Pointer (напр. `/users/0/email`), позволяя сразу перейти к точному узлу во вложенных данных
  • Изменённые записи показывают значения «до» И «после» рядом, так что очевидно, переключился ли 'active' с true→false или изменилось число
  • Сводка: добавлено, удалено, изменено и неизменные листья — масштаб diff одним взглядом перед чтением деталей по путям
  • Раздельный отчёт об ошибке разбора по стороне называет, какой ввод не разобрался, и включает сообщение движка JavaScript — не нужно угадывать, A или B повреждён
  • Кнопка «Копировать отчёт» выдаёт весь diff (разделы «Добавлено» / «Удалено» / «Изменено» плюс счётчик неизменных) как простой текст — вставьте в код-ревью или сообщение коммита
  • «Загрузить пример» заполняет обе панели маленьким реалистичным примером (запись пользователя с изменениями роли + адреса), новые пользователи видят diff без набора текста
  • Полностью на стороне клиента: JSON A и JSON B парсятся и сравниваются в вашем браузере; ничего не загружается, и инструмент работает офлайн после загрузки страницы

Как использовать

  1. Вставьте JSON «до» в левое текстовое поле (JSON A), JSON «после» — в правое (JSON B).
  2. Нажмите «Сравнить». Diff рассчитывается рекурсивно: объекты — по ключу, массивы — по индексу.
  3. Прочтите сводку: сколько ключей добавлено, удалено, изменено и сколько листьев совпало.
  4. Прокрутите списки по путям, чтобы увидеть, какие пути JSON Pointer изменились; изменённые строки показывают «до» → «после».
  5. Нажмите «Копировать отчёт», чтобы получить весь diff простым текстом — удобно для заметок ревью, баг-репортов и сообщений коммитов.
  6. Используйте «Загрузить пример», чтобы увидеть рабочий пример перед вставкой своих данных.

Советы и лучшие практики

  • Сначала прогоните обе стороны через JSON Formatter, если хотите, чтобы текстовый diff тоже считал структурно равные документы без изменений.
  • Для больших массивов с важным порядком этот инструмент подходит идеально; для множеств — предварительно отсортируйте по стабильному ключу.
  • Вывод пути — RFC 6901, его можно передать напрямую в библиотеки вроде fast-json-patch для программного применения изменений.
  • Вставляйте отчёт в описания PR, чтобы показать ревьюерам, что именно изменилось в JSON конфига или фикстуры.
  • Если ошибки разбора указывают на конкретную колонку, сообщение движка часто включает строку/колонку у парсеров типа json5 — здесь же используется строгий JSON.parse, поэтому строка/колонка — от хоста.

Вопросы и ответы

Как сравниваются массивы — по индексу или по значению?

По индексу. Элемент 0 A сравнивается с элементом 0 B, 1 с 1 и т. д. Если A длиннее, лишние хвостовые индексы помечаются как «Удалено»; если B длиннее — как «Добавлено». Быстро и предсказуемо, но единичная вставка в начале большого массива каскадирует во множество «изменено». Для нечувствительных к порядку сравнений (множества уникальных ID) сначала отсортируйте оба массива или преобразуйте в объекты с ключом-идентификатором.

Какая нотация путей в результате?

RFC 6901 JSON Pointer: пути — это токены, разделённые слешами, спускающиеся по дереву JSON. `/users/0/email` означает «свойство email элемента 0 в users». Ключи с `~` экранируются как `~0`, слеши — `~1`. Сам корень — `/`. Та же нотация используется в патч-инструментах, JSON Schema instancePath и в подсказке указателя json-formatter.

Как обрабатывается null?

null трактуется как листовое значение, не как объект — хотя в JavaScript typeof null === 'object'. Так {a: null} vs {a: {}} показывается как изменённое, а не рекурсирует в пустой объект. {a: null} vs {a: null} — один неизменный лист, а {a: null} vs {a: 0} — изменённое с «до=null после=0».

Diff обрабатывает ключи со специальными символами?

Да. Ключи, содержащие точки, слеши, скобки или тильды, экранируются по JSON Pointer в выводе пути — `~` становится `~0`, `/` — `~1`. Так ключ с буквальным именем `a/b` показывается как `/a~1b` — однозначно обратимо.

Почему счёт неизменных не соответствует моим ожиданиям?

Неизменные считаются на уровне ЛИСТЬЕВ — примитивы, равные между A и B. Глубоко вложенный объект, чьё поддерево полностью идентично, даёт по одной неизменной записи на лист, а не одну на всё поддерево. Это делает счётчик настоящей проверкой: «сколько примитивных значений осталось прежними».

Что если одна сторона null или пуста?

Пустой ввод парсится как null (так `{a:1}` vs пусто сравнивается с null и весь объект помечается как добавленный). Литеральный `null` — валидный JSON-документ; сравнение двух null даёт ноль добавленных/удалённых/изменённых и один неизменный. Если оба пусты — diff тривиально нулевой везде.

Мой JSON куда-то отправляется?

Нет. Оба текстовых поля остаются в вашем браузере; diff работает в том же JavaScript-контексте, что и страница. Вкладка «Сеть» в DevTools не показывает запросов при нажатии «Сравнить». Инструмент работает офлайн после кеширования страницы.

Чем это отличается от построчного текстового diff?

Текстовый diff вроде `diff a.json b.json` работает с символами и переводами строк: переформатируете те же данные — он подсветится. Это СТРУКТУРНЫЙ diff: `{"a":1, "b":2}` и `{"b":2, "a":1}` идентичны (ноль diff). Также не учитывает отступы, концевые пробелы и стили комментариев. Используйте текстовый diff, когда важно форматирование; этот — когда важны данные.