Gibt es Befehlszeilenprogramme, mit denen ermittelt werden kann, ob zwei JSON-Dateien mit der Invarianz der Reihenfolge innerhalb des Wörterbuchschlüssels und innerhalb des Listenelements identisch sind?
Könnte dies mit jq
oder einem anderen gleichwertigen Werkzeug durchgeführt werden?
Beispiele:
Diese beiden JSON-Dateien sind identisch
A
::
{
"People": ["John", "Bryan"],
"City": "Boston",
"State": "MA"
}
B
::
{
"People": ["Bryan", "John"],
"State": "MA",
"City": "Boston"
}
Diese beiden JSON-Dateien sind jedoch unterschiedlich:
A
::
{
"People": ["John", "Bryan", "Carla"],
"City": "Boston",
"State": "MA"
}
C
::
{
"People": ["Bryan", "John"],
"State": "MA",
"City": "Boston"
}
Das wäre:
$ some_diff_command A.json B.json
$ some_diff_command A.json C.json
The files are not structurally identical
--argfile a a.json
um--arg a $a
ohne Glück (als $ aa json string). Irgendeine Idee, wie man sich Strings nähert, nicht Dateien?--argjson
ArgumentWenn Sie Zugriff auf Bash oder eine andere erweiterte Shell haben, können Sie im Prinzip so etwas tun
cmp <(jq -cS . A.json) <(jq -cS . B.json)
unter Verwendung von Unterprozessen. Dadurch wird der JSON mit sortierten Schlüsseln und einer konsistenten Darstellung von Gleitkommazahlen formatiert. Dies sind die einzigen zwei Gründe, warum json mit demselben Inhalt unterschiedlich gedruckt wird. Wenn Sie anschließend einen einfachen Zeichenfolgenvergleich durchführen, erhalten Sie einen ordnungsgemäßen Test. Es ist wahrscheinlich auch erwähnenswert, dass wenn Sie Bash nicht verwenden können, Sie mit temporären Dateien dieselben Ergebnisse erzielen können, es einfach nicht so sauber ist.
Dies beantwortet Ihre Frage nicht ganz, da Sie die gewünschte Frage gestellt
["John", "Bryan"]
und["Bryan", "John"]
identisch verglichen haben. Da json nicht das Konzept einer Menge hat, sondern nur eine Liste, sollten diese als unterschiedlich betrachtet werden. Reihenfolge ist wichtig für Listen. Sie müssten einen benutzerdefinierten Vergleich schreiben, wenn Sie möchten, dass sie gleichermaßen verglichen werden, und dazu müssten Sie definieren, was Sie unter Gleichheit verstehen. Ist die Reihenfolge für alle Listen oder nur für einige wichtig? Was ist mit doppelten Elementen? Wenn Sie möchten, dass sie als Menge dargestellt werden und die Elemente Zeichenfolgen sind, können Sie sie auch in Objekte wie setzen{"John": null, "Bryan": null}
. Die Reihenfolge spielt beim Vergleich der Gleichheit keine Rolle.Aktualisieren
Aus der Kommentardiskussion: Wenn Sie eine bessere Vorstellung davon bekommen möchten, warum der JSON nicht derselbe ist, dann
diff <(jq -S . A.json) <(jq -S . B.json)
erzeugt eine besser interpretierbare Ausgabe.
vimdiff
Je nach Geschmack ist es möglicherweise vorzuziehen, sich zu unterscheiden.quelle
jq
jq
es Argumente im Posix-Stil gibt muss anrufenjq -c -S ...
vimdiff <(jq -S . a.json) <(jq -S . b.json)
-c
(was die Ausgabe kompakt macht). Stileinstellungen sind für Ihre Antwort nicht relevant.c
nicht unbedingt notwendig ist, aber für mich gibt es keinen Grund für cmp, identische Leerzeichen zu vergleichen, und keinen Grund für jq, sich die Mühe zu machen, es auszugeben.diff
,vimdiff
Oder irgendein Werkzeug , das Dateivergleich tut funktioniert, abercmp
ist alles , was notwendig ist.Verwenden Sie
jd
mit der-set
Option:Keine Ausgabe bedeutet keinen Unterschied.
$ jd -set A.json B.json
Unterschiede werden als @ -Pfad und + oder - angezeigt.
$ jd -set A.json C.json @ ["People",{}] + "Carla"
Die Ausgabedifferenzen können mit dieser
-p
Option auch als Patch-Dateien verwendet werden .$ jd -set -o patch A.json C.json; jd -set -p patch B.json {"City":"Boston","People":["John","Carla","Bryan"],"State":"MA"}
https://github.com/josephburnett/jd#command-line-usage
quelle
diff
formatierungskompatible Ausgabe. Tolle.Es gibt eine Antwort auf diese hier , die nützlich sein würde.
Im Wesentlichen können Sie die Git-
diff
Funktionalität verwenden (auch für nicht von Git verfolgte Dateien), die auch Farbe in die Ausgabe einbezieht:git diff --no-index payload_1.json payload_2.json
quelle
Hier ist eine Lösung mit der generischen Funktion walk / 1 :
# Apply f to composite entities recursively, and to atoms def walk(f): . as $in | if type == "object" then reduce keys[] as $key ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f elif type == "array" then map( walk(f) ) | f else f end; def normalize: walk(if type == "array" then sort else . end); # Test whether the input and argument are equivalent # in the sense that ordering within lists is immaterial: def equiv(x): normalize == (x | normalize);
Beispiel:
{"a":[1,2,[3,4]]} | equiv( {"a": [[4,3], 2,1]} )
produziert:
true
Und als Bash-Skript verpackt:
#!/bin/bash JQ=/usr/local/bin/jq BN=$(basename $0) function help { cat <<EOF Syntax: $0 file1 file2 The two files are assumed each to contain one JSON entity. This script reports whether the two entities are equivalent in the sense that their normalized values are equal, where normalization of all component arrays is achieved by recursively sorting them, innermost first. This script assumes that the jq of interest is $JQ if it exists and otherwise that it is on the PATH. EOF exit } if [ ! -x "$JQ" ] ; then JQ=jq ; fi function die { echo "$BN: $@" >&2 ; exit 1 ; } if [ $# != 2 -o "$1" = -h -o "$1" = --help ] ; then help ; exit ; fi test -f "$1" || die "unable to find $1" test -f "$2" || die "unable to find $2" $JQ -r -n --argfile A "$1" --argfile B "$2" -f <(cat<<"EOF" # Apply f to composite entities recursively, and to atoms def walk(f): . as $in | if type == "object" then reduce keys[] as $key ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f elif type == "array" then map( walk(f) ) | f else f end; def normalize: walk(if type == "array" then sort else . end); # Test whether the input and argument are equivalent # in the sense that ordering within lists is immaterial: def equiv(x): normalize == (x | normalize); if $A | equiv($B) then empty else "\($A) is not equivalent to \($B)" end EOF )
POSTSCRIPT: walk / 1 ist eine integrierte Version von jq> 1.5 und kann daher weggelassen werden, wenn Ihr jq es enthält, aber es schadet nicht, es redundant in ein jq-Skript aufzunehmen.
POST-POSTSCRIPT: Die integrierte Version von
walk
wurde kürzlich geändert, sodass die Schlüssel innerhalb eines Objekts nicht mehr sortiert werden. Insbesondere verwendet eskeys_unsorted
. Für die jeweilige Aufgabekeys
sollte die verwendete Version verwendet werden.quelle
walk
in jq 1.5 hinzugefügt wurde. Ich habe mir einen Kompromissoperator zwischenfilter
und gewünschtmap
und es sieht so aus, als ob es das ist.Vielleicht könnten Sie dieses Sortier- und Diff-Tool verwenden: http://novicelab.org/jsonsortdiff/, das die Objekte zuerst semantisch sortiert und dann vergleicht. Es basiert auf https://www.npmjs.com/package/jsonabc
quelle
Ziehen Sie das Beste aus den beiden besten Antworten heraus, um ein
jq
basiertes JSON-Diff zu erhalten:diff \ <(jq -S 'def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (. | (post_recurse | arrays) |= sort)' "$original_json") \ <(jq -S 'def post_recurse(f): def r: (f | select(. != null) | r), .; r; def post_recurse: post_recurse(.[]?); (. | (post_recurse | arrays) |= sort)' "$changed_json")
Dies erfordert die elegante Array-Sortierlösung von https://stackoverflow.com/a/31933234/538507 (mit der wir Arrays als Sets behandeln können) und die saubere Bash-Umleitung
diff
von https://stackoverflow.com/a/37175540/ 538507 Dies betrifft den Fall, in dem Sie einen Unterschied von zwei JSON-Dateien wünschen und die Reihenfolge des Array-Inhalts nicht relevant ist.quelle
Wenn Sie auch die Unterschiede sehen möchten, verwenden Sie die Antwort von @ Erik als Inspiration und js-verschönern :
$ echo '[{"name": "John", "age": 56}, {"name": "Mary", "age": 67}]' > file1.json $ echo '[{"age": 56, "name": "John"}, {"name": "Mary", "age": 61}]' > file2.json $ diff -u --color \ <(jq -cS . file1.json | js-beautify -f -) \ <(jq -cS . file2.json | js-beautify -f -) --- /dev/fd/63 2016-10-18 13:03:59.397451598 +0200 +++ /dev/fd/62 2016-10-18 13:03:59.397451598 +0200 @@ -2,6 +2,6 @@ "age": 56, "name": "John Smith" }, { - "age": 67, + "age": 61, "name": "Mary Stuart" }]
quelle
-c
von derjq
Kommandozeile. Ich weiß nicht, lieber keine zusätzlichen unnötigen Werkzeuge einführen;)Ein weiteres Tool für diejenigen, zu denen die vorherigen Antworten nicht passen, können Sie jdd ausprobieren .
Es basiert auf HTML, sodass Sie es entweder online unter www.jsondiff.com verwenden können. Wenn Sie es lieber lokal ausführen möchten , laden Sie einfach das Projekt herunter und öffnen Sie die Datei index.html.
quelle