API-Antworten, Konfigurationsdateien, Übersetzungsdateien — Daten im JSON-Format muss man oft vergleichen. Vergleicht man zwei JSON-Dokumente aber einfach als Text, erscheinen oft zahlreiche Unterschiede, obwohl die Daten gleich sind. Dieser Artikel erklärt, warum das so ist und wie man es löst.
Warum Scheinunterschiede entstehen
Der JSON-Standard (RFC 8259) definiert ein Objekt als „ungeordnete Sammlung von Name/Wert-Paaren“. {"a":1,"b":2} und {"b":2,"a":1} bedeuten also dieselben Daten. Als Text unterscheidet sich jedoch die Reihenfolge der Zeilen, und diff meldet einen Unterschied. Auch die folgenden Unterschiede entstehen unabhängig von den Daten:
- Einrückungsbreite (2 Leerzeichen, 4 Leerzeichen, Tab) und auf eine Zeile komprimiertes JSON
- Leerzeichen nach dem Doppelpunkt oder nicht (
"a":1und"a": 1) - Zahlenschreibweise (
1.0und1,1e2und100) - Escape-Sequenzen in Zeichenketten (
"\u00e9"und"é")
Die Lösung: parsen und einheitlich neu schreiben
Am zuverlässigsten ist es, beide JSON-Dokumente zu parsen und anschließend nach denselben Regeln neu zu serialisieren.
- Beide Texte als JSON parsen. Syntaxfehler zuerst beheben.
- Die Schlüssel aller Objekte rekursiv sortieren.
- Mit derselben Einrückung (z. B. 2 Leerzeichen) neu schreiben.
- Das Ergebnis zeilenweise vergleichen.
Die beiden folgenden JSON-Dokumente unterscheiden sich zum Beispiel in der Reihenfolge der Schlüssel und in der Reihenfolge eines Arrays.
{"name":"kim","age":30,"tags":["a","b"]}
{"age":30,"name":"kim","tags":["b","a"]}
Nach dem Sortieren und Neuschreiben verschwindet der Unterschied in der Schlüsselreihenfolge, und nur der echte Unterschied, die Array-Reihenfolge, bleibt übrig.
@@ -2,7 +2,7 @@ "age": 30, "name": "kim", "tags": [- "a",- "b"+ "b",+ "a" ] }
Arrays werden nicht sortiert
Anders als bei Objekten hat die Reihenfolge in Arrays eine Bedeutung. ["a","b"] und ["b","a"] sind unterschiedliche Daten. Beim Normalisieren sortiert man Arrays daher grundsätzlich nicht. Handelt es sich aber um ein Array, dessen Reihenfolge fachlich keine Rolle spielt — etwa eine Liste von Tags —, wird das Ergebnis leichter lesbar, wenn Sie es vor dem Vergleich selbst sortieren. Was richtig ist, hängt von der Bedeutung der Daten ab.
Was das Parsen verändert
Das Parsen und Neuschreiben hat Nebenwirkungen, die man kennen sollte.
- Die Zahlenschreibweise wird vereinheitlicht.
1.0und1werden beide zu1und erscheinen gleich. Meist ist das erwünscht; ist die Schreibweise selbst wichtig, vergleichen Sie zusätzlich den Originaltext. - Genauigkeit großer Ganzzahlen. JavaScript stellt Zahlen als 64-Bit-Gleitkommazahlen dar und kann Ganzzahlen größer als
Number.MAX_SAFE_INTEGER(2^53 − 1 = 9007199254740991) nicht exakt abbilden.9007199254740993wird beim Parsen zum Beispiel zu9007199254740992. Achten Sie darauf bei JSON, das lange IDs als Zahlen enthält. - Doppelte Schlüssel. RFC 8259 empfiehlt (SHOULD) lediglich, dass Namen innerhalb eines Objekts eindeutig sind; wie doppelte Schlüssel behandelt werden, hängt von der Implementierung ab.
JSON.parsein JavaScript behält den letzten Wert. - Kommentare und abschließende Kommas.
// Kommentarund abschließende Kommas wie in[1, 2,]sind kein Standard-JSON (erweiterte Formate wie JSON5 oder JSONC erlauben sie). Ein Standard-Parser meldet einen Fehler.
Auf der Kommandozeile
Ist jq installiert, gibt die Option -S (--sort-keys) die Daten mit sortierten Schlüsseln aus.
jq -S . before.json > a.json
jq -S . after.json > b.json
diff -u a.json b.json
Unterschied zu JSON Patch
Ein diff-Ergebnis ist eine zeilenweise Liste von Änderungen, die Menschen lesen sollen. JSON Patch (RFC 6902) dagegen ist ein Standard, der Änderungen über Pfade in der JSON-Struktur ausdrückt, etwa {"op":"replace","path":"/age","value":31}, und JSON Merge Patch (RFC 7396) überschreibt das Original mit einem JSON, das nur die geänderten Teile enthält. Tauschen Programme Änderungen untereinander aus, sind diese Standards passend; prüft ein Mensch, eignet sich ein Text-diff nach dem Sortieren.
In diesem Werkzeug ausprobieren
Aktivieren Sie in den Optionen des Textvergleichs JSON-Schlüssel sortieren: Beide Seiten werden geparst, die Schlüssel sortiert, mit 2 Leerzeichen Einrückung neu geschrieben und dann verglichen. Die Array-Reihenfolge bleibt unverändert, und schlägt das Parsen fehl, erfahren Sie, auf welcher Seite und an welchem Zeichen der Fehler liegt. Sie können auch zwei .json-Dateien auf einmal hineinziehen. Wie man das Ergebnis als Patch liest, erklärt Unified Diff lesen.