it-swarm.com.ru

Преобразование CSV в JSON в Bash

Попытка преобразовать файл CSV в JSON 

Вот две примерные строки: 

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

Я хотел бы получить что-то вроде: 

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

Вот мой сценарий: 

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

Я получаю действительно странные результаты: 

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

Что не так с моим кодом? 

10
HydrUra

Правильный инструмент для этой работы - jq .

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

испускает, учитывая ваш вклад:

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

Кстати, менее оригинальная версия вашего скрипта может выглядеть так:

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

Замечания:

  • Абсолютно бессмысленно использовать cat для передачи в цикл (и веские причины не делать ); таким образом, мы используем перенаправление (<), чтобы открыть файл непосредственно как стандартный цикл.
  • read может быть передан список переменных назначения; Таким образом, нет необходимости читать в массив (или first, чтобы читать в строку, а затем генерировать гистерезис и читать из него в массив). _ в конце гарантирует, что лишние столбцы отбрасываются (помещая их в фиктивную переменную с именем _), а не добавляются к pos.
  • "${array[*]}" генерирует строку путем объединения элементов array с символом в IFS; таким образом, мы можем использовать это, чтобы гарантировать, что запятые присутствуют в выводе только тогда, когда они необходимы.
  • printf используется вместо echo, как указано в разделе ИСПОЛЬЗОВАНИЕ ПРИЛОЖЕНИЯ спецификации для самой echo .
  • Это по-прежнему является ошибкой, поскольку генерирует JSON через конкатенацию строк. Не используйте это.
10
Charles Duffy

Вот статья на эту тему: https://infiniteundo.com/post/99336704013/convert-csv-to-json-with-jq

Он также использует JQ, но немного другой подход, используя split() и map().

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

Однако он не обрабатывает выход разделителя.

3
Ondra Žižka

В общем, если ваш jq имеет встроенный фильтр inputs (доступен с jq 1.5), то лучше использовать его, а не параметр командной строки -s. 

Здесь в любом случае есть решение с использованием inputs. Это решение также без переменных.

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

SSV, CSV и все такое

Выше, конечно, предполагается, что файл имеет разделенные точкой с запятой поля в каждой строке, и что нет никаких сложностей, связанных с файлами CSV.

Если у ввода есть поля, которые строго ограничены одним символом, то у jq не должно возникнуть проблем с обработкой. В противном случае может быть лучше использовать инструмент, который может надежно конвертировать в формат TSV (значение, разделенное табуляцией), который jq может обрабатывать напрямую. 

0
peak

Поскольку решение jq не обрабатывает экранирование CSV, имена столбцов в первой строке, закомментированные строки и другие распространенные CSV-функции, я расширил инструмент CSV Cruncher , чтобы он мог читать CSV и записывать его как JSON. Это не совсем "Bash", но и jq :)

В первую очередь это приложение для обработки CSV-as-SQL, так что это не совсем тривиально, но вот хитрость:

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

Он также позволяет выводить как JSON-объект на строку или правильный JSON-массив. Смотрите документацию.

Он находится в бета-версии, поэтому приветствуются все пожелания или отзывы.

0
Ondra Žižka