Zwei JSON-Strukturen vergleichen
- Dashboard
- Dokumentation
- API
Warum zwei JSON-Dokumente vergleichen?
Zwei JSON-Strukturen zu vergleichen kommt im Alltag eines Entwicklers regelmäßig vor. Eine API-Antwort, die sich nach einem Update ändert. Eine Konfigurationsdatei, die zwischen zwei Umgebungen divergiert. Ein Objektexport, der an einer Referenz ausgerichtet werden muss. Ein JSON-Diff beantwortet genau diese Frage: Was wurde hinzugefügt, entfernt oder geändert, und an welcher Stelle?
Ein zeilenweiser diff (à la git diff) reicht oft nicht aus. Wenn die Formatierung
abweicht (Leerzeichen, Schlüsselreihenfolge), meldet der textuelle Diff Hunderte von Zeilen, obwohl die Datenstruktur
identisch ist. Ein JSON-Vergleicher arbeitet auf der einmal geparsten Struktur, was dieses
Rauschen eliminiert und nur die semantischen Abweichungen aufdeckt.
Das vom Tool erzeugte Diff-Format
Für jede Abweichung gibt das Tool zurück:
- einen Pfad im vereinfachten JSONPath-Format, zum Beispiel
$.user.address[0].city; - einen Typ aus added (rechts hinzugefügt), removed (nur links vorhanden), modified (unterschiedliche Werte);
- den linken Wert und/oder den rechten Wert je nach Typ.
Ein leeres Diff bedeutet, dass die beiden JSON-Dokumente strukturell identisch sind, unabhängig von ihrer Formatierung oder der Schlüsselreihenfolge.
Wie der Algorithmus die beiden Strukturen durchläuft
Der Algorithmus ist rekursiv. Auf jeder Ebene identifiziert er den Typ der beiden verglichenen Werte:
- Wenn beide assoziative Objekte sind, bildet er die Vereinigung der Schlüssel. Für jeden Schlüssel steigt er rekursiv ab oder markiert added/removed, wenn der Schlüssel nur auf einer Seite existiert.
- Wenn beide geordnete Arrays sind, vergleicht er Position für Position. Eine Differenz am Array-Anfang kann den gesamten Rest verschieben, was zu einem ausführlichen Diff führt: Das ist eine bewusste Grenze des naiven strukturellen Diff.
- Wenn die Typen abweichen (Objekt gegen Array, Skalar gegen null), wird es als modified markiert.
- Wenn beide Werte Skalare sind (Zeichenkette, Zahl, Boolean, null), genügt ein einfacher strikter Vergleich.
Die Schlüsselreihenfolge in einem Objekt zählt nicht: {"a": 1, "b": 2} und {"b": 2, "a": 1}
erzeugen ein leeres Diff. Das entspricht der JSON-Semantik, wo die Reihenfolge nicht signifikant ist. Hingegen
zählt die Reihenfolge der Elemente eines Arrays: Ein Array ist konstruktionsbedingt geordnet.
Ein konkretes Beispiel
Hier sind zwei Versionen eines Benutzerobjekts:
// gauche
{
"id": 42,
"name": "Alice",
"roles": ["admin", "editor"]
}
// droite
{
"id": 42,
"name": "Alice Martin",
"roles": ["admin", "viewer"],
"active": true
}
Das erzeugte Diff:
$.name: modified,"Alice"→"Alice Martin"$.roles[1]: modified,"editor"→"viewer"$.active: added,true
Anwendungsfälle
- Umgebungs-Diff: die Ausgabe eines Endpunkts in Pre-Production und Production vergleichen. Sehr nützlich bei einer Migration oder einer Cache-Aktualisierung.
- Migrations-Audit: einen Export vor und nach der Transformation vergleichen, um zu prüfen, dass kein Feld verloren gegangen ist.
- API-Regression: vor und nach einer Änderung die Antwort für eine identische Anfrage vergleichen. Ein leeres Diff bestätigt die Nicht-Regression.
- Konfigurationssynchronisation:
composer.jsonzwischen zwei Branches, zwei.eslintrc-Dateien, zwei Symfony-Konfigurationen vergleichen. - Snapshot-Tests: einen zeilenweisen Vergleich durch einen strukturellen Vergleich in einer Integrationstest-Suite ersetzen.
Grenzen des strukturellen Diff
Geordnete Arrays zu vergleichen ist eine bekannte Grenze naiver struktureller Diffs. Wenn man ein Element am Array-Anfang einfügt, werden alle folgenden Positionen verschoben und das Diff meldet jede Abweichung als Änderung. Für solche Fälle existieren fortgeschrittenere Algorithmen (Myers, Patience, Diff nach natürlichem Schlüssel), die jedoch den Rahmen eines allgemeinen Vergleichstools sprengen.
Das Diff sagt auch nicht, warum eine Änderung stattgefunden hat: Es ist eine Feststellung. Um eine Regression zu analysieren, muss man diese Feststellung mit Commits, Deployments und Abfrage-Parametern abgleichen.
JSON-Diff vs. JSON Patch (RFC 6902)
Ein ergänzendes Format ist JSON Patch (RFC 6902). Es beschreibt in Form von Operationen
(add, remove, replace, move, copy,
test), wie ein JSON-Dokument in ein anderes transformiert wird. Während unser Diff eine Feststellung
ist (menschlich), ist JSON Patch ein Rezept (maschinell). Beide Darstellungen sind für einfache Fälle
gleichwertig, und JSON Patch ist nützlich für RESTful-APIs, die partielle Änderungen akzeptieren.
Häufig gestellte Fragen
Hängt das Diff von der Schlüsselreihenfolge ab?
Nein: Bei einem JSON-Objekt ist die Reihenfolge nicht signifikant. Der Vergleicher erzeugt dasselbe Ergebnis, ob die Schlüssel sortiert sind oder nicht.
Wie geht man mit Arrays um, deren Reihenfolge keine Rolle spielt?
Das Tool betrachtet Arrays standardmäßig als geordnet (das ist die JSON-Semantik). Wenn Sie Mengen verarbeiten, sortieren Sie beide Arrays vor dem Vergleich nach einem natürlichen Schlüssel oder verwenden Sie einen spezialisierten Vergleichsdienst, der diese Semantik berücksichtigt.
Was ist der Unterschied zu einem Git-Diff?
Git vergleicht Textzeilen. Wenn Einrückung oder Schlüsselreihenfolge abweichen, ist das Git-Diff sehr ausführlich. Das JSON-Diff arbeitet auf der geparsten Struktur und meldet nur die Datenabweichungen.
Wird ein ungültiges JSON akzeptiert?
Nein: Wenn eines der beiden JSON-Dokumente nicht parsen lässt, gibt das Tool einen Fehler zurück. Validieren Sie zuerst mit unserem JSON-Validator.
Beispielanfrage
curl -X POST https://cdrn.fr/api/v1/tools/json-diff/execute \
-H "Content-Type: application/json" \
-d '{"left":"...","right":"..."}'
Eingabeschema
| Feld | Typ | Erforderlich | Standard |
|---|---|---|---|
left |
text | ✓ | – |
right |
text | ✓ | – |
Endpunkte
GET https://cdrn.fr/api/v1/tools- listet alle verfügbaren Tools aufGET https://cdrn.fr/api/v1/tools/json-diff- liefert das Schema dieses ToolsPOST https://cdrn.fr/api/v1/tools/json-diff/execute- führt dieses Tool mit einem JSON-Payload aus