it-swarm.com.de

Verwenden von jq zum Extrahieren von Werten und Formatieren in CSV

Ich habe die folgende JSON-Datei:

{
"data": [
    {
        "displayName": "First Name",
        "rank": 1,
        "value": "VALUE"
    },
    {
        "displayName": "Last Name",
        "rank": 2,
        "value": "VALUE"
    },
    {
        "displayName": "Position",
        "rank": 3,
        "value": "VALUE"
    },
    {
        "displayName": "Company Name",
        "rank": 4,
        "value": "VALUE"
    },
    {
        "displayName": "Country",
        "rank": 5,
        "value": "VALUE"
    },
]
}

Ich hätte gerne eine CSV-Datei in diesem Format:

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE, VALUE

Ist dies möglich, wenn nur jq verwendet wird? Ich habe keine Programmierkenntnisse.

62
Kerim

jq hat den Filter @csv zum Konvertieren eines Arrays in eine CSV-Zeichenfolge. Dieser Filter berücksichtigt die meisten Komplexitäten, die mit dem CSV-Format verbunden sind, beginnend mit in Felder eingebetteten Kommas. (jq 1.5 hat einen ähnlichen Filter, @tsv, zum Generieren von Dateien mit durch Tabulatoren getrennten Werten.)

Wenn die Überschriften und Werte garantiert alle frei von Kommas und doppelten Anführungszeichen sind, muss der @ csv-Filter möglicherweise nicht verwendet werden. Andernfalls wäre es wahrscheinlich besser, es zu verwenden.

Wenn der 'Firmenname' beispielsweise 'Smith, Smith and Smith' wäre und die anderen Werte wie unten gezeigt wären, würde das Aufrufen von jq mit der Option "-r" eine gültige CSV erzeugen:

$ jq -r '.data | map(.displayName), map(.value) | @csv' so.json2csv.json
"First Name","Last Name","Position","Company Name","Country"
"John (""Johnnie"")","Doe","Director, Planning and Posterity","Smith, Smith and Smith","Transylvania"
55
peak

Ich ziehe es vor, jeden Datensatz in meiner CSV zu einer Zeile zu machen.

jq '.data | map([.displayName, .rank, .value] | join(", ")) | join("\n")'
36
Silas Paul

Mit nur dieser Datei können Sie Folgendes tun:

<testfile jq -r '.data | map(.displayName), map(.value) | join(", ")'

Das . Operator wählt ein Feld aus einem Objekt/Hash aus. Wir beginnen also mit .data, das das Array mit den darin enthaltenen Daten zurückgibt. Wir ordnen das Array dann zweimal zu, wählen zuerst den Anzeigenamen und dann den Wert aus und erhalten zwei Arrays mit nur den Werten dieser Schlüssel. Für jedes Array verbinden wir die Elemente mit "," und bilden zwei Linien. Das -r Argument weist jq an, die resultierenden Zeichenfolgen nicht in Anführungszeichen zu setzen.

Wenn Ihre eigentliche Datei länger ist (dh Einträge für mehr als eine Person enthält), benötigen Sie wahrscheinlich etwas Komplizierteres.

31
Steven D

Ich habe jq schwer gefunden, meinen Kopf herumzuwickeln. Hier ist etwas Ruby:

Ruby -rjson -rcsv -e '
  data = JSON.parse(File.read "file.json")
  data["data"].collect {|item| [item["displayName"], item["value"]]}
              .transpose
              .each {|row| puts row.to_csv}
'
First Name,Last Name,Position,Company Name,Country
VALUE,VALUE,VALUE,VALUE,VALUE

Der JSON-Parser Ruby] hat das abschließende Komma vor der schließenden Klammer gesperrt.

11
glenn jackman

Da Sie diese python markiert haben und davon ausgehen, dass der Name der json -Datei x.json Ist

import os, json
with open('x.json') as f:
    x  = json.load(f)
    print '{}{}{}'.format(', '.join(y['displayName'] for y in x['data']), os.linesep,
             ', '.join(y['value'] for y in x['data']))
First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE
2
iruvar

Obwohl ich das letzte Komma in Ihrer Beispieleingabe entfernen musste, damit es funktioniert, weil jq sich über die Erwartung eines anderen Array-Elements beschwert hat:

INPUT | jq -r '[.[][].displayName], [.[][].value]| join(", ")'

...hast mich...

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE

Wie es auf den Punkt gebracht funktioniert:

  1. Ich bin mit dem leeren Indexfeldformular [] Und der Notation .dot Zur dritten Ebene der Datenobjekte durchgegangen.
  2. Einmal tief genug, habe ich die gewünschten Datenfelder mit Namen wie .[][].displayName Angegeben.
  3. Ich versicherte, dass meine gewünschten Felder selbst zugeordnet waren, indem ich sie als separate Array-Objekte wie [.[][].displayName], [.[][].value] Zurückgab.
  4. Anschließend werden diese Objekte an die Funktion join(", ") weitergeleitet, um sie als separate Entitäten zu verbinden.

In Wahrheit ist [.field] Nur eine andere Möglichkeit, map(.field) zu verwenden. Dies ist jedoch insofern etwas spezifischer, als es die Tiefenstufe zum Abrufen der gewünschten Daten angibt.

1
mikeserv