web-dev-qa-db-fra.com

Conversion de CSV en JSON en bash

Essayer de convertir un fichier CSV en JSON 

Voici deux exemples de lignes: 

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

J'aimerais avoir quelque chose comme: 

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

Voici mon script: 

    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 "]";

J'obtiens des résultats vraiment étranges: 

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

Quel est le problème avec mon code? 

10
HydrUra

Le bon outil pour ce travail est jq .

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

émet, compte tenu de votre entrée:

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

À propos, une version moins boguée de votre script original pourrait ressembler à ceci:

#!/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[*]}"

Remarque:

  • Il est absolument inutile d'utiliser cat pour insérer une boucle (et de bonnes raisons de ne pas ); ainsi, nous utilisons une redirection (<) pour ouvrir le fichier directement en tant que stdin de la boucle.
  • read peut recevoir une liste de variables de destination; il n'y a donc pas besoin de lire dans un tableau (ou first pour lire dans une chaîne, puis de générer un heresting et d'en lire un dans un tableau). Le _ à la fin garantit que les colonnes supplémentaires sont supprimées (en les plaçant dans la variable factice nommée _) plutôt que ajoutées à pos.
  • "${array[*]}" génère une chaîne en concaténant des éléments de array avec le caractère dans IFS; nous pouvons donc l'utiliser pour nous assurer que les virgules ne sont présentes dans la sortie que lorsqu'elles sont nécessaires.
  • printf est utilisé de préférence à echo, comme indiqué dans la section UTILISATION D'APPLICATION de la spécification de echo elle-même .
  • Cela pose toujours un problème car il génère du JSON via la concaténation de chaînes. Ne l'utilisez pas.
10
Charles Duffy

Voici un article sur le sujet: https://infiniteundo.com/post/99336704013/convert-csv-to-json-with-jq

Il utilise également JQ, mais une approche légèrement différente utilisant split() et map().

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

Cependant, il ne gère pas l'échappement du séparateur.

3
Ondra Žižka

En général, si votre jq a le filtre intégré inputs (disponible depuis jq 1.5), il est préférable de l’utiliser plutôt que l’option de ligne de commande -s. 

Ici, dans tous les cas, une solution utilisant inputs. Cette solution est également sans variable.

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

SSV, CSV et tout ça

Ce qui précède suppose bien sûr que le fichier comporte des champs séparés par des points-virgules dans chaque ligne et qu’il n’ya aucune des complications associées aux fichiers CSV.

Si l'entrée comporte des champs strictement délimités par un seul caractère, jq ne devrait pas rencontrer de problèmes pour le gérer. Sinon, il peut être préférable d’utiliser un outil capable de convertir de manière fiable le format TSV (valeur séparée par des tabulations), que jq peut gérer directement. 

0
peak

Étant donné que la solution jq ne gère pas les échappements CSV, les noms de colonne de la première ligne, les lignes de commentaire et les autres "fonctionnalités" CSV courantes, j'ai étendu l'outil CSV Cruncher pour autoriser la lecture de CSV et son écriture au format JSON. Ce n'est pas exactement "Bash", mais jq non plus :)

Il s'agit principalement d'une application de traitement CSV en tant que SQL. Ce n'est donc pas complètement trivial, mais voici le truc:

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

Il permet également la sortie sous forme d'objet JSON par ligne ou de tableau JSON approprié. Voir la documentation.

Il est en version bêta, donc tous les commentaires ou demandes d’extraction sont les bienvenus.

0
Ondra Žižka