在bash中将CSV转换为JSON

Hyd*_*Ura 14 unix csv bash json jq

尝试将CS​​V文件转换为JSON

这是两个示例行:

-21.3214077;55.4851413;Ruizia cordata
-21.3213078;55.4849803;Cossinia pinnata
Run Code Online (Sandbox Code Playgroud)

我想得到类似的东西:

"occurrences": [
                 {
                "position": [-21.3214077, 55.4851413],
                "taxo": {
                    "espece": "Ruizia cordata"
                 },
                 ...
             }]
Run Code Online (Sandbox Code Playgroud)

这是我的脚本:

    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 "]";
Run Code Online (Sandbox Code Playgroud)

我得到了非常奇怪的结果:

   "occurences": [ 
 ""position": [ -21.3214077, 55.4851413 ], "taxo": {"espece":" Ruizia cordata
 ""position": [ -21.3213078, 55.4849803 ], "taxo": {"espece":" Cossinia pinnata
Run Code Online (Sandbox Code Playgroud)

我的代码出了什么问题?

jst*_*aab 14

这是一个可以解决问题的python单行/脚本:

cat my.csv | python -c 'import csv, json, sys; print(json.dumps([dict(r) for r in csv.DictReader(sys.stdin)]))
Run Code Online (Sandbox Code Playgroud)

  • 要将其写入文件只需添加`| &gt; filename.json` 在最后。像这样:`cat my.csv | python -c '导入 csv、json、sys;print(json.dumps([csv.DictReader(sys.stdin) 中的 r 的 dict(r)]))' | &gt; my.json` (3认同)
  • @tink imo,将标头添加到 .csv 文件比使用复杂的 jq 查询更容易 (3认同)
  • 这应该是公认的解决方案——它对任何和所有有效负载都有效。jq版本是一次性的,需要煞费苦心地匹配模式 (2认同)

Cha*_*ffy 13

这项工作的正确工具是jq.

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

在您输入的情况下发出:

{
  "occurences": [
    {
      "position": [
        "-21.3214077",
        "55.4851413"
      ],
      "taxo": {
        "espece": "Ruizia cordata"
      }
    },
    {
      "position": [
        "-21.3213078",
        "55.4849803"
      ],
      "taxo": {
        "espece": "Cossinia pinnata"
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,原始脚本的错误版本可能如下:

#!/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[*]}"
Run Code Online (Sandbox Code Playgroud)

注意:

  • 使用cat管道进入循环是绝对没有意义的(并且有很好的理由); 因此,我们使用重定向(<)直接打开文件作为循环的标准输入.
  • read可以传递目标变量列表; 因此,不需要读入数组(或者首先读取一个字符串,然后生成一个heresting并从中读取到一个数组中).所述_在端部保证了额外的列将被抛弃(通过将它们到名为虚变量_),而不是附加到pos.
  • "${array[*]}"通过连接array字符的元素生成一个字符串IFS; 因此,我们可以使用它来确保只在需要时输出中存在逗号.
  • printfecho如在本说明书echo的"应用程序使用"部分中所建议的那样,优先使用.
  • 这仍然是错误的,因为它通过字符串连接生成JSON.不要使用它.


ric*_*ler 13

John Kerl 的Miller工具内置了以下功能:

mlr --c2j --jlistwrap cat INPUT.csv > OUTPUT.json
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢“jq”,但这真的很好,至少对于将带有 CSV 的列标题转换为 JSON 来说是这样。@理查德克米勒 (2认同)

Ram*_*man 11

接受的答案用于jq解析输入。这有效但jq不处理转义,即从 Excel 或类似工具生成的 CSV 输入的引用如下:

foo,"bar,baz",gaz
Run Code Online (Sandbox Code Playgroud)

将导致错误的输出,因为 jq 将看到 4 个字段,而不是 3 个。

一种选择是使用制表符分隔值而不是逗号(只要您的输入数据不包含制表符!),以及接受的答案。

另一种选择是组合您的工具,并为每个部分使用最好的工具:CSV 解析器,用于读取输入并将其转换为 JSON,并将jqJSON 转换为目标格式。

基于 python 的csvkit将智能地解析 CSV,并附带一个工具csvjson,可以更好地将 CSV 转换为 JSON。然后可以通过 jq 将其通过管道传输,以将 csvkit 的平面 JSON 输出转换为目标形式。

使用 OP 提供的数据,对于所需的输出,这很简单:

csvjson --no-header-row  |
  jq '.[] | {occurrences: [{ position: [.a, .b], taxo: {espece: .c}}]}'
Run Code Online (Sandbox Code Playgroud)

需要注意的是csvjson自动检测;作为分隔符,并且没有标题行中的输入,分配JSON键作为ab,和c

这同样也适用于写CSV文件-csvkit可以读取一个JSON数组或新线过分隔的JSON,并智能输出CSV in2csv


Ond*_*žka 5

这是一篇关于这个主题的文章: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
Run Code Online (Sandbox Code Playgroud)

但是,它不处理分隔符转义。


Tsu*_*oka 5

这是 Ruby 的单行解决方案:

ruby -r json -r csv -e 'puts CSV.parse(STDIN, headers:true).map(&:to_h).to_json' < INPUT.csv 
Run Code Online (Sandbox Code Playgroud)