Výběr nástroje pro JSON diff: textový diff, strukturální diff a vizualizace
Shrnutí: Textový diff najde, kde se soubory liší; strukturální diff rozhoduje, zda jsou obsahy ekvivalentní. Volba správné vrstvy zabrání tomu, aby se pořadí klíčů považovalo za incident.

Stručně: textový diff objevuje změnu (nejprve seřaďte klíče pomocí jq -S), strukturální diff rozhoduje o ekvivalenci (pole seřaďte podle primárního klíče) a vizualizace změnu pouze vysvětluje nebo reviduje; tři vrstvy udržujte v CI oddělené.
Dva JSON dokumenty, které vypadají odlišně, nemusí mít odlišný obsah. Než zvolíte nástroj, zvolte správnou vrstvu srovnání.
1. Tři druhy „diffu“ nejsou totéž
Mnoho týmů považuje „srovnat JSON“ za jednu činnost a poté v různých scénářích naráží na stejné problémy. Pomáhá oddělit tři vrstvy:
| Vrstva | Co se srovnává | Co odhaluje | Typické použití |
|---|---|---|---|
| Textový diff | Surové bajty a řádky | Odsazení, pořadí klíčů, mezery, zalomení řádků | Rozdíly v konfiguraci, revize kódu |
| Strukturální diff | Parsované objekty a pole | Přidané nebo odebrané klíče, změněné hodnoty, změny prvků pole | Regrese API, migrace dat |
| Vizuální diff | Jak jsou rozdíly prezentovány | Do které úrovně změna patří, jak velký má dopad | Lidská revize, týmová diskuse |
Textový diff je bez závislostí, rychlý a přirozeně se integruje s Git. Jeho slabinou je, že nezná sémantiku JSON. Dva zcela ekvivalentní dokumenty se rozsvítí červeně a zeleně, pokud se liší pořadí klíčů nebo odsazení. Strukturální diff je opak: nejprve parsuje a poté srovnává, takže rozpozná, že se hodnota jen přesunula. Potřebuje však správnou strategii zarovnání polí; jinak se jedno vložení přečte jako úplný přepis.
Vizualizace není třetí algoritmus. Vykresluje výstup ostatních dvou vrstev, aby člověk rychle potvrdil, zda je rozdíl očekávaný. Je dobrá pro lidský úsudek a špatná jako náhrada automatizovaných kontrol. Smíchání vrstev obvykle vede k CI pipeline, která je trvale červená, a k týmu, který se naučil ji ignorovat.
2. Textový diff: nejprve ukliďte pořadí klíčů pomocí jq -S
Pro rychlý pohled na dva soubory je nejlevnější kombinací jq -S plus diff -u. Přepínač -S rekurzivně seřadí klíče objektů a normalizuje dokumenty, jejichž obsah je shodný, ale pořadí klíčů odlišné:
jq -S . a.json > a.sorted.json
jq -S . b.json > b.sorted.json
diff -u a.sorted.json b.sorted.json
Tím se odstraní nejčastější falešný poplach. RFC 8259 definuje objekty jako neuspořádané kolekce, takže {"a":1,"b":2} a {"b":2,"a":1} jsou stejná hodnota. Textový diff však označí každý řádek jako změněný. Seřazení předem posune červené bloky mnohem blíž skutečným problémům.
Pozor, jq -S řadí jen klíče; neřadí pole. Pořadí pole má v JSON význam a [1,2] není stejná hodnota jako [2,1], takže nástroj toto rozhodnutí neudělá za vás. Pokud je pole konceptuálně neuspořádaná množina, seřaďte ho podle primárního klíče sami, než ho srovnáte.
Je tu ještě jeden detail, který stojí za zapamatování: diff skončí s kódem 1, když najde rozdíly, ale chybějící soubor nebo chyba oprávnění také produkuje nenulový kód. V CI nepovažujte každý nenulový kód za „obsah se změnil“, jinak se překlep v cestě ohlásí jako změna dat. Bezpečnější vzor nejprve ověří soubory a poté řeší rozdíl:
if ! diff -u a.sorted.json b.sorted.json; then
echo "Obsah JSON se liší" >&2
exit 1
fi
delta dělá textový diff čitelným
Surový výstup diff -u funguje, ale dlouhé řádky a hluboce vnořený JSON se čtou únavně. delta je filtr diffu, který přidává zvýrazňování syntaxe, čísla řádků a jasnější barvy pozadí:
diff -u a.sorted.json b.sorted.json | delta
Začínal jako pager pro Git, takže umí převzít i git diff. Pro lidi, kteří denně čtou deset konfiguračních diffů, takové nástroje zlepšují rychlost čtení, nikoli algoritmickou přesnost. Další běžnou volbou je git diff --no-index --word-diff, který srovnává na úrovni slov a dobře funguje pro zminifikovaný JSON uložený na velmi dlouhých řádcích.
Jedno varování: zkrášlení neodstraňuje falešné rozdíly. Pokud pořadí klíčů nebylo normalizováno, delta jen udělá šum hezčím. Správné pořadí je vždy nejprve normalizovat, poté zdobit.
3. Strukturální diff: jsondiffpatch rozumí sémantice
Když je otázkou, zda jsou dvě data sémanticky ekvivalentní, použijte strukturální diff. Nejběžnější volbou v ekosystému JavaScriptu je jsondiffpatch, který vrací delta objekt popisující změny, nikoli textové řádky se značkami + a -:
import { create } from 'jsondiffpatch';
const instance = create();
const delta = instance.diff(
{ id: 1, tags: ['a', 'b'], enabled: true },
{ id: 1, tags: ['a', 'c'], enabled: false },
);
console.log(JSON.stringify(delta, null, 2));
Delta rozlišuje přidané klíče, odebrané klíče, nahrazené hodnoty a přesunuté prvky pole. To znamená, že deltu můžete procházet přímo v assertionech: undefined, když není rozdíl, nebo popis cestu po cestě, když je. Na rozdíl od textového diffu ho neruší odsazení, pořadí klíčů ani číselný zápis jako 1e3 versus 1000.
Stejně důležité je, že výstup strukturálního diffu může živit pracovní postup s patchi. jsondiffpatch poskytuje patch a reverse, takže delta lze aplikovat na původní objekt a později vrátit. Jedno upozornění: automaticky vygenerovaná delta nemá stejný formát jako ručně psaný JSON Patch a převod mezi nimi něco stojí. Širšímu přístupu ke srovnávání se věnuje Diff a srovnávání JSON.
4. Jak odstranit falešné rozdíly způsobené pořadím klíčů
Pořadí klíčů není jen otravné; může vést ke špatným závěrům. Při vyšetřování postupujte ve třech krocích:
- Před srovnáním rekurzivně normalizujte pořadí klíčů pomocí
jq -Snebo ekvivalentu. - Odstraňte pole, která se mění při každém běhu, například
timestamp,requestIdatraceId. - Potvrďte, že neexistují duplicitní klíče. Duplicitní klíče se během parsování vyřeší pravidlem „poslední vyhrává“, takže textový diff je vidí, zatímco strukturální ne.
jq -S 'del(.meta.timestamp, .meta.requestId)' a.json > /tmp/a.json
jq -S 'del(.meta.timestamp, .meta.requestId)' b.json > /tmp/b.json
diff -u /tmp/a.json /tmp/b.json
Tento postup se dobře hodí pro rozdíly v konfiguraci. Prožeňte staging i produkci stejnou normalizací a co zůstane, je rozdíl, který skutečně potřebuje vysvětlení. Pravidla filtru udržujte ve verzovaném skriptu, místo abyste je znovu psali zpaměti. Editační a skriptovací části nástrojového řetězce se věnuje Editory, příkazový řádek a automatizace.
5. Pole: zarovnat podle indexu, nebo podle id?
Pole jsou nejchybovější částí srovnávání JSON. Při výchozím zarovnání podle indexu vložení jednoho záznamu na začátek posune všechny následující prvky a nástroj je označí jako změněné. Nástroj není rozbitý; strategie zarovnání je špatná.
| Zarovnání | Funguje, když | Přednost | Riziko |
|---|---|---|---|
| Podle indexu | Pořadí pole nese obchodní význam | Jednoduché, nevyžaduje další pole | Jedno vložení způsobí kaskádové falešné poplachy |
| Podle primárního klíče | Prvky jsou množina s jedinečným id | Detekuje vložení, smazání a přesuny | Vyžaduje stabilní jedinečný klíč |
| Nejdelší společná podposloupnost | Pořadí je důležité, ale vložení jsou povolena | Robustnější než čisté indexování | Hůř vysvětlitelné, vyšší náklady |
jsondiffpatch používá objectHash k deklaraci primárního klíče a indexuje prvky pole před srovnáním:
const instance = create({
objectHash: (item: { id?: string; sku?: string }) => item.id ?? item.sku,
});
S prostým jq můžete nejprve seřadit a poté použít textový diff:
jq -S 'sort_by(.id)' items.json > items.sorted.json
Kompromis u zarovnání podle id je, že změny pozice se už nepočítají jako rozdíly. Pokud obchod potřebuje vědět, že se záznam přesunul z pozice 2 na pozici 5, srovnávejte také explicitní pole pořadí; nespoléhejte jen na obsah množiny.
6. Vizuální diff: vrstva postavená pro lidi
Výstup strukturálního diffu je navržen pro stroje, nikoli pro porady. Když potřebujete někomu jinému vysvětlit změnu, vizualizace je účinnější:
- Diff editor ve VS Code se hodí pro revizi soubor po souboru, s panely vedle sebe a inline zvýrazněním.
- Stromové prohlížeče jako JSON Crack a JSON Hero ukazují, do které úrovně změna patří.
jsondiffpatchobsahuje HTML formatter, který vykreslí deltu jako sbalitelný bohatý náhled.
Hodnotou vizualizace je kontext. Textový diff říká, že se změnil řádek 88; stromové zobrazení říká, že řádek 88 patří do data.items[3].price. Vizualizace však obvykle načítá celý dokument do prohlížeče nebo desktopové aplikace. Než otevřete produkční payload, ověřte, zda nástroj počítá lokálně, nebo nahrává na server.
7. Tři zásady pro zařazení diffu do CI
Za prvé, normalizujte před srovnáním. Zafixujte odsazení, rekurzivně seřaďte klíče a odfiltrujte volatilní pole dřív, než se diff spustí. Jinak CI opakovaně selhává na šumu.
Za druhé, volte zarovnání podle sémantiky. Pokud je pole množinou, zarovnávejte podle primárního klíče. Pokud je posloupností, ponechte zarovnání podle indexu. Neaplikujte jedno pravidlo na každé API.
Za třetí, tvrdte hodnoty, nikoli text. Srovnávání parsovaných objektů v testech je mnohem stabilnější než srovnávání výstupu JSON.stringify. Když kontrola selže, vypište cestu rozdílu a hodnoty před a po, místo abyste do logu vysypali celý payload.
# Vypište jen lišící se cesty a hodnoty, nikoli celý dokument
jq -S 'del(.timestamp)' actual.json > /tmp/actual.json
jq -S 'del(.timestamp)' expected.json > /tmp/expected.json
diff -u /tmp/expected.json /tmp/actual.json | head -n 80
Pokud rozdíl vyžaduje dlouhodobé sledování, committed normalizační skript a očekávaný soubor společně. Pokud jde o jednorázové vyšetřování, dočasný skript poté smažte, aby se tiše nestal druhou implementací obchodní logiky.
8. Tabulka výběru nástroje
| Scénář | První volba | Alternativa | Klíčové připomenutí |
|---|---|---|---|
| Občasné srovnání souborů | diff ve VS Code | diff -u |
Nejprve naformátujte, abyste odstranili šum |
| Rozdíly v konfiguraci | jq -S + diff |
delta |
Filtrujte volatilní pole |
| Regrese odpovědí API | jsondiffpatch |
deepdiff |
Pole zarovnejte podle primárního klíče |
| Vysvětlení změny týmu | JSON Crack / JSON Hero | HTML formatter | Ověřte, zda se data nahrávají |
| Velmi velké soubory | extrakce podstromu pomocí jq |
prohlížení v jless |
Nenačítejte celý strom do paměti |
| Kontrola před commitem | git diff --no-index |
pre-commit | Explicitně ošetřete návratové kódy |
Neexistuje jediný nejlepší nástroj pro JSON diff; existuje jen správný nástroj pro aktuální vrstvu. Textový diff objevuje změnu, strukturální diff soudí ekvivalenci a vizualizace změnu vysvětluje. Udržovat tři vrstvy oddělené je mnohem spolehlivější než hledat jeden nástroj, který dělá všechno. Řešením konfliktů napříč větvemi se pokračuje v článku JSON a sloučení v Git.
Časté dotazy
Mění jq -S také pořadí pole?
Ne. -S řadí jen klíče objektů a pole ponechává v původním pořadí. Pole jsou v JSON uspořádaná, takže nástroj nezmění [1,2] na [2,1]. Pokud je pole konceptuálně neuspořádaná množina, seřaďte ho explicitně pomocí sort_by.
Jsou 1 a 1.0 ve strukturálním diffu stejná hodnota?
Většina parserů obě převede na stejné číslo, takže strukturální diff je obvykle považuje za rovné, zatímco textový diff je označí jako rozdílné. Strukturální diff používejte k rozhodnutí o ekvivalenci a textový diff k rozhodnutí, zda jsou soubory bajtově identické.
Lze výstup diffu použít přímo jako JSON Patch?
Ne přímo. Nástroj diffu hlásí, co se liší, zatímco JSON Patch popisuje uspořádaný seznam operací; formáty a sémantika se liší. Automaticky vygenerované patche navíc obvykle odkazují na indexy polí, které při souběžnosti zastarají. Před aplikací přidejte operaci test.
Jak srovnávat JSON soubory o velikosti desítek MB?
Nejprve extrahujte podstrom, který vás zajímá, například pomocí jq '.data.items', poté seřaďte podle primárního klíče a srovnejte. Nenačítejte celý strom do paměti pro rekurzivní diff. Velmi velké soubory se lépe zpracovávají streamováním nebo dávkovým srovnáváním a paměťové náklady vyžadují samostatné posouzení.
Mohou online nástroje pro diff uniknout moje data?
Záleží na implementaci. Čistě frontendové nástroje počítají v prohlížeči a data nenahrávají; serverově vykreslované nástroje posílají obsah na backend, kde se může dostat do logů. Než srovnáte produkční konfiguraci nebo zákaznická data, ověřte, že stránka počítá lokálně, a v případě potřeby to otestujte jednou s odpojenou sítí.
Jak se rozhodnout mezi textovým a strukturálním diffem?
Pro rychlý pohled, kde se dva soubory liší, seřaďte klíče pomocí jq -S a spusťte diff -u. K rozhodnutí, zda jsou dva dokumenty sémanticky ekvivalentní, použijte strukturální diff, například jsondiffpatch, a pole zarovnejte podle primárního klíče. Textový diff je bez závislostí a integruje se s Git, zatímco strukturální diff neruší odsazení, pořadí klíčů ani číselný zápis.
Proč by CI nemělo považovat každý nenulový návratový kód diffu za rozdíl?
diff skončí s 1, když najde rozdíly, ale chybějící soubor nebo chyba oprávnění také vrací nenulový kód. Považovat každý nenulový kód za „obsah se změnil“ ohlásí překlep v cestě jako změnu dat. Bezpečnější vzor nejprve ověří soubory a poté rozdíl řeší zvlášť.
Mám srovnávat soubory o desítkách MB v prohlížeči?
Ne. Nejprve extrahujte podstrom, který vás zajímá, pomocí jq, poté seřaďte podle primárního klíče a srovnejte. Načtení celého stromu do paměti pro rekurzivní diff stojí mnohem více paměti a času. Velmi velké soubory se lépe zpracovávají streamováním nebo dávkovým srovnáváním.