web-dev-qa-db-de.com

Konvertieren von CSV in JSON in Bash

Versuch, eine CSV-Datei in eine JSON-Datei zu konvertieren 

Hier sind zwei Beispielzeilen: 

-21.3214077;55.4851413;Ruizia cordata
-21.3213078;55.4849803;Cossinia pinnata

Ich würde gerne etwas bekommen wie: 

"occurrences": [
                 {
                "position": [-21.3214077, 55.4851413],
                "taxo": {
                    "espece": "Ruizia cordata"
                 },
                 ...
             }]

Hier ist mein Skript: 

    echo '"occurences": [ '

cat se.csv | while read -r line
  do
      IFS=';' read -r -a array <<< $line;
      echo -n -e '{ "position": [' ${array[0]}
      echo -n -e ',' ${array[1]} ']'
      echo -e ', "taxo": {"espece":"' ${array[2]} '"'
done
echo "]";

Ich bekomme wirklich seltsame Ergebnisse: 

   "occurences": [ 
 ""position": [ -21.3214077, 55.4851413 ], "taxo": {"espece":" Ruizia cordata
 ""position": [ -21.3213078, 55.4849803 ], "taxo": {"espece":" Cossinia pinnata

Was stimmt nicht mit meinem Code? 

10
HydrUra

Das richtige Werkzeug für diesen Job ist jq .

jq -Rsn '
  {"occurrences":
    [inputs
     | . / "\n"
     | (.[] | select(length > 0) | . / ";") as $input
     | {"position": [$input[0], $input[1]], "taxo": {"espece": $input[2]}}]}
' <se.csv

sendet, wenn Sie Ihre Eingabe eingeben:

{
  "occurences": [
    {
      "position": [
        "-21.3214077",
        "55.4851413"
      ],
      "taxo": {
        "espece": "Ruizia cordata"
      }
    },
    {
      "position": [
        "-21.3213078",
        "55.4849803"
      ],
      "taxo": {
        "espece": "Cossinia pinnata"
      }
    }
  ]
}

Eine weniger fehlerhafte Version Ihres Originalskripts könnte übrigens so aussehen:

#!/usr/bin/env bash

items=( )
while IFS=';' read -r lat long pos _; do
  printf -v item '{ "position": [%s, %s], "taxo": {"espece": "%s"}}' "$lat" "$long" "$pos"
  items+=( "$item" )
done <se.csv

IFS=','
printf '{"occurrences": [%s]}\n' "${items[*]}"

Hinweis:

  • Es ist absolut sinnlos, cat zu verwenden, um in eine Schleife zu leiten (und gute Gründe, nicht zu ); Daher verwenden wir eine Umleitung (<), um die Datei direkt als stdin der Schleife zu öffnen.
  • read kann eine Liste von Zielvariablen übergeben werden; Es ist daher nicht notwendig, in ein Array zu lesen (oder first, um einen String einzulesen und dann ein Heresting zu erzeugen und daraus in ein Array zu lesen). Der _ am Ende stellt sicher, dass zusätzliche Spalten verworfen werden (indem sie in die Dummy-Variable _ eingefügt werden), anstatt an pos angehängt zu werden.
  • "${array[*]}" generiert eine Zeichenfolge, indem Elemente von array mit dem Zeichen in IFS verkettet werden. Damit können wir sicherstellen, dass Kommas nur dann in der Ausgabe vorhanden sind, wenn sie benötigt werden.
  • printf wird gegenüber echo bevorzugt verwendet, wie im Abschnitt ANWENDUNGSBEREICH von der Spezifikation für echo selbst empfohlen.
  • Dies ist immer noch fehlerhaft, da JSON über String-Verkettung generiert wird. Verwenden Sie es nicht.
10
Charles Duffy

Hier ist ein Artikel zum Thema: https://infiniteundo.com/post/99336704013/convert-csv-to-json-with-jq

Es verwendet auch JQ, aber ein etwas anderer Ansatz mit split() und map().

jq --Slurp --raw-input \
   'split("\n") | .[1:] | map(split(";")) |
      map({
         "position": [.[0], .[1]],
         "taxo": {
             "espece": .[2]
          }
      })' \
  input.csv > output.json

Es kann jedoch nicht mit dem Trennzeichen umgehen.

3
Ondra Žižka

Wenn Ihr jq über den eingebauten Filter inputs verfügt (verfügbar seit jq 1.5), ist es im Allgemeinen besser, ihn anstelle der Befehlszeilenoption -s zu verwenden. 

Hier ist auf jeden Fall eine Lösung mit inputs. Diese Lösung ist auch variablenfrei.

{"occurrences":
  [inputs
   | select(length > 0)
   | . / ";"
   | {"position": [.[0], .[1]], 
      "taxo": {"espece": .[2]}} ]}

SSV, CSV und so weiter

Bei den obigen Ausführungen wird natürlich davon ausgegangen, dass die Datei in jeder Zeile durch Semikolons getrennte Felder enthält und dass keine der mit CSV-Dateien verbundenen Komplikationen auftritt.

Wenn die Eingabe Felder enthält, die streng durch ein einzelnes Zeichen begrenzt sind, sollte jq keine Probleme damit haben. Andernfalls empfiehlt es sich, ein Tool zu verwenden, das zuverlässig in das TSV-Format (durch Tabulatoren getrennte Werte) konvertiert werden kann, mit dem jq direkt umgehen kann. 

0
peak

Da die Lösung jq CSV-Escapeing, Spaltennamen in der ersten Zeile, auskommentierte Zeilen und andere gebräuchliche CSV- "Features" nicht verarbeitet, habe ich das Werkzeug CSV Cruncher um das Lesen von CSV und das Schreiben als erweitert JSON. Es ist nicht genau "Bash", aber auch nicht jq :)

Es ist in erster Linie eine CSV-as-SQL-Verarbeitungs-App, also nicht völlig trivial, aber hier ist der Trick:

./crunch -in myfile.csv -out output.csv --json -sql 'SELECT * FROM myfile'

Es erlaubt auch die Ausgabe als JSON-Objekt pro Zeile oder als richtiges JSON-Array. Siehe die Dokumentation.

Es ist in Beta-Qualität, daher sind alle Rückmeldungen oder Pull-Anfragen willkommen.

0
Ondra Žižka