Wie konvertieren von beliebigen einfachen JSON, CSV mit jq?
Mit jq, wie kann beliebige JSON-Kodierung ein array von flache Objekte umgewandelt werden, um CSV?
Es gibt viele F&auf dieser Website, die auf spezifische Daten-Modelle, die hard-code die Felder, aber die Antworten auf diese Frage soll die Arbeit gegeben, JSON, mit der einzigen Einschränkung, dass es ist ein array von Objekten mit skalaren Eigenschaften (keine tiefen/Komplex/sub-Objekte, wie diese Abflachung ist eine andere Frage). Das Ergebnis sollte eine Kopfzeile geben Sie die Feldnamen. Bevorzugt werden Antworten, dass die Erhaltung der Feld-Reihenfolge der das erste Objekt, aber es ist keine Voraussetzung. Ergebnisse können schließen Sie alle Zellen mit doppelten Anführungszeichen, oder nur schließen diejenigen, die das setzen in Anführungszeichen benötigen (z.B. 'a,b').
Beispiele
-
Eingang:
[ {"code": "NSW", "name": "New South Wales", "level":"state", "country": "AU"}, {"code": "AB", "name": "Alberta", "level":"province", "country": "CA"}, {"code": "ABD", "name": "Aberdeenshire", "level":"council area", "country": "GB"}, {"code": "AK", "name": "Alaska", "level":"state", "country": "US"} ]
Möglich-Ausgang:
code,name,level,country NSW,New South Wales,state,AU AB,Alberta,province,CA ABD,Aberdeenshire,council area,GB AK,Alaska,state,US
Möglich-Ausgang:
"code","name","level","country" "NSW","New South Wales","state","AU" "AB","Alberta","province","CA" "ABD","Aberdeenshire","council area","GB" "AK","Alaska","state","US"
-
Eingang:
[ {"name": "bang", "value": "!", "level": 0}, {"name": "letters", "value": "a,b,c", "level": 0}, {"name": "letters", "value": "x,y,z", "level": 1}, {"name": "bang", "value": "\"!\"", "level": 1} ]
Möglich-Ausgang:
name,value,level bang,!,0 letters,"a,b,c",0 letters,"x,y,z",1 bang,"""!""",0
Möglich-Ausgang:
"name","value","level" "bang","!","0" "letters","a,b,c","0" "letters","x,y,z","1" "bang","""!""","1"
- Drei-plus Jahre später ... eine generische
json2csv
ist unter stackoverflow.com/questions/57242240/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Erstens erhalten Sie ein array mit allen unterschiedlichen Objekt-Namen für die Eigenschaft in das Objekt-array-Eingang. Diese werden die Spalten der CSV:
Dann für jedes Objekt in der Objekt-array input, ordnen Sie die Spaltennamen, die Sie erhalten, um die entsprechenden Eigenschaften in das Objekt. Diese werden die Zeilen der CSV.
Schließlich, legen Sie die Spalte Namen vor, die Zeilen als eine Kopfzeile für die CSV, und übergeben Sie die resultierende Zeile-stream auf der
@csv
filter.Jetzt alle zusammen. Vergessen Sie nicht, die
-r
flag, um das Ergebnis als raw-string:$rows
variable Zuordnung nur durch inlining es:(map(keys) | add | unique) as $cols | $cols, map(. as $row | $cols | map($row[.]))[] | @csv
$rows
nicht einer Variablen zugewiesen werden; ich dachte nur, Zuweisung an eine variable aus der Erklärung schöner.my.json > my.csv
auf der gleichen Linie?Die Skinny
oder:
Die Details
Beiseite
Beschreibung der details ist schwierig, da jq ist stream-orientiert, d.h. Sie arbeitet auf einer Sequenz von JSON-Daten, anstatt einen einzelnen Wert. Der JSON-input-stream umgewandelt wird, um einige interne Art, die übergeben wird, durch die Filter, und kodiert in einem output-stream bei Programm-Ende. Der interne Typ ist nicht modelliert, indem JSON, und existiert nicht als Namen geben. Es ist sehr leicht bewiesen durch die Untersuchung der Ausgabe eines bloßen index (
.[]
) oder den Komma-operator (Prüfung es direkt getan werden könnte, mit einem debugger, aber das wäre in Bezug auf die jq-internen Datentypen, anstatt die konzeptionellen Daten-Typen hinter JSON).Beachten Sie, dass die Ausgabe nicht ein array (das wäre
["a", "b"]
). Kompakte Ausgabe (die-c
- option) zeigt, dass jedes element des Arrays (oder argument, um die,
filter) wird ein separates Objekt in der Ausgabe (jede ist eine separate Linie).Einen Strom wie ein JSON-seq, aber verwendet Zeilenvorschub anstatt RS als output-separator, wenn codiert. Damit ist der interne Typ ist bezeichnet durch den Allgemeinen Begriff "Sequenz" in dieser Antwort, mit "stream" vorbehalten sind, die codierte Eingabe und Ausgabe.
Bau den Filter
Das erste Objekt ist der Schlüssel extrahiert werden können, mit:
Schlüssel werden in der Regel gehalten in Ihrem ursprünglichen Auftrag, aber gleichzeitig wird die genaue Reihenfolge ist nicht garantiert. Folglich werden Sie brauchen, um verwendet werden, um den index der Objekte zu erhalten, die Werte in der gleichen Reihenfolge. Dies wird auch verhindern, dass Werte in den falschen Spalten, wenn einige Objekte haben verschiedene Schlüssel um.
Sowohl die Ausgabe der Schlüssel in der ersten Zeile und stellen Sie Sie für die Indizierung, sind Sie in einer Variablen gespeichert. Die nächste Stufe der pipeline, dann verweisen diese Variablen und verwendet den Komma-operator vorangestellt wird, die header zu den Ausgabe-stream.
Den Ausdruck nach dem Komma ist ein wenig beteiligt. Der index-operator auf ein Objekt kann eine Sequenz von Zeichenfolgen (z.B.
"name", "value"
), die RÜCKFÜHRUNG einer Folge von property-Werten für diese Saiten.$keys
ist ein array, keine Sequenz, so[]
angewendet wird, es zu konvertieren, um eine Sequenz,kann dann übergeben werden, die
.[]
Auch dies erzeugt eine Reihenfolge, so dass die array-Konstruktor verwendet wird, konvertieren es in ein array.
Dieser Ausdruck wird angewendet, um ein einzelnes Objekt.
map()
wird verwendet, um wenden Sie es auf alle Objekte in der äußeren array:Schließlich für diese Phase, dieser ist in einer Sequenz, so ist jedes Element wird eine separate Zeile in der Ausgabe.
Warum bündeln Sie die Sequenz in ein array innerhalb der
map
nur zu Entbündeln außerhalb?map
erzeugt ein array;.[ $keys[] ]
entsteht eine Folge. Die Anwendungmap
zu der Sequenz von.[ $keys[] ]
produzieren würde ein array von Sequenzen von Werten, aber da die Sequenzen, die nicht ein JSON-Typ, so dass Sie stattdessen einen abgeflachten array mit allen Werten.Die Werte jedes Objekts werden müssen, getrennt gehalten, so dass Sie sich zu separaten Zeilen in der endgültigen Ausgabe.
Schließlich die Sequenz Durchlaufen
@csv
formatter.Alternativen
Elemente können getrennt zu spät, statt zu früh. Statt mit dem Komma-operator zu erhalten, eine Sequenz (vorbei an einer Sequenz, wie der Rechte operand), der header Sequenz (
$keys
) aufgewickelt werden kann, in ein array, und+
anfügen array von Werten. Dies muss noch umgewandelt werden in eine Sequenz vor der übergabe an@csv
.keys_unsorted
stattkeys
zu bewahren, der Schlüssel, um vom ersten Objekt?$ echo '{"a":1,"b":2,"c":3}' |jq -r '(. | keys_unsorted) as $keys| $keys, map( [.[ $keys[] ] ])[] | @csv'
Ausgänge"a","b","c" jq: error (at <stdin>:1): Cannot index number with string "a"
auf jq-1.5.[{"a":1,"b":2,"c":3}]
.Den folgenden filter ist etwas anders, dass es wird sichergestellt, dass jeder Wert wird in einen string umgewandelt. (Hinweis: verwenden jq 1.5+)
Filter:
filter.jq
unique
sortiert ist sowieso, sounique|sort
kann vereinfacht werden zuunique
.-r
option. Ansonsten alle Zitate"
werden extra entgangen, die ist nicht gültig CSV.Erstellte ich eine Funktion, die als Ausgabe ein array von Objekten oder arrays in csv mit Header. Die Spalten werden in der Reihenfolge der Kopfzeilen.
So könnten Sie es etwa so:
Diese Variante von Santiago Programm ist auch sicher, aber es wird sichergestellt, dass die key-Namen in
das erste Objekt dient als erste Spaltenüberschriften, in der gleichen Reihenfolge, wie
erscheinen in diesem Objekt: