使用jq将CSV转换为JSON

使用jq将CSV转换为JSON,json,csv,jq,Json,Csv,Jq,如果您有如下csv数据集: name, age, gender john, 20, male jane, 30, female bob, 25, male 你能做到这一点吗: [ {"name": "john", "age": 20, "gender": "male"}, {"name": "jane", "age": 30, &q

如果您有如下csv数据集:

name, age, gender
john, 20, male
jane, 30, female
bob, 25, male
你能做到这一点吗:

[ {"name": "john", "age": 20, "gender": "male"},
  {"name": "jane", "age": 30, "gender": "female"},
  {"name": "bob", "age": 25, "gender": "male"} ]
只使用jq

我找到了一篇文章,它展示了我要做的事情,但它使用了头字段到值的“手动”映射。我不需要/不想重命名标题字段,并且有很多标题字段。我也不希望每次布局更改时都必须更改脚本/命令


是否可以动态提取标题,然后用jq单行程序将其与值组合起来?

我做了一点尝试,想出了这个方法。但这可能不是最好的方法,我很想看看你的尝试是什么样的,因为毕竟如果我们都能找到一个解决方案,我相信它会是原来的两倍

但我会从以下几点开始:

true as $doHeaders
| . / "\n"
| map(. / ", ")
| (if $doHeaders then .[0] else [range(0; (.[0] | length)) | tostring] end) as $headers
| .[if $doHeaders then 1 else 0 end:][]
| . as $values
| keys
| map({($headers[.]): $values[.]})

变量
$doHeaders
控制是否将顶行作为标题行读取。在你的例子中,你希望它是真实的,但我为未来的用户添加了它,因为,嗯,我今天吃了一顿很好的早餐,天气很好,为什么不呢

小解释:

1)
/“\n”
按行拆分

2)
映射(./“,”)
。。。和逗号(Big-gotcha:在您的版本中,您需要使用基于正则表达式的拆分,因为这样您也可以在引号内使用逗号进行拆分。我之所以使用它,是因为它很简洁,这使我的解决方案看起来很酷,对吗?)

3)
如果$doHeaders,那么…
这里我们根据第一行中的元素数量以及第一行是否为标题行创建一个字符串、键或数字数组

4)
[如果是$doHeaders,那么1 else 0 end:][/code>好的,如果是页眉,请修剪掉顶行

5)
map({($headers[.]):$values[.]})
在上面,我们检查前一个csv中的每一行,并将
$values
放入一个变量中,将键放入一个管道中。然后我们构造你想要的对象


当然,您会希望使用一些正则表达式来填充gotchas,但我希望这会让您开始学习。

简言之,是的,可能除了一行代码位

jq通常非常适合文本争论,对于支持regex的版本尤其如此。例如,对于正则表达式支持,给定问题语句所需的修剪是微不足道的

由于jq 1.5rc1包含正则表达式支持,并且自2015年1月1日起开始提供,因此以下程序采用jq 1.5版本;如果您希望使它与JQ1.4一起工作,那么请参阅两条“For JQ1.4”注释

还请注意,该程序并不处理CSV的所有通用性和复杂性。(对于处理CSV的类似方法,请参阅)

示例(假设csv.csv是给定的csv文本文件):


这里是一个假设您使用
-s
-R
选项运行jq的解决方案

[
  [                                               
    split("\n")[]                  # transform csv input into array
  | split(", ")                    # where first element has key names
  | select(length==3)              # and other elements have values
  ]                                
  | {h:.[0], v:.[1:][]}            # {h:[keys], v:[values]}
  | [.h, (.v|map(tonumber?//.))]   # [ [keys], [values] ]
  | [ transpose[]                  # [ [key,value], [key,value], ... ]
      | {key:.[0], value:.[1]}     # [ {"key":key, "value":value}, ... ]
    ]
  | from_entries                   # { key:value, key:value, ... }
]
样本运行:

jq -s -R -f filter.jq data.csv
样本输出

[
  {
    "name": "john",
    "age": 20,
    "gender": "male"
  },
  {
    "name": "jane",
    "age": 30,
    "gender": "female"
  },
  {
    "name": "bob",
    "age": 25,
    "gender": "male"
  }
]

到2018年,一个现代的无代码解决方案将是使用Python工具
csvkit
has
csvjson data.csv>data.json

请参阅他们的文档

如果脚本必须同时调试
csv
json
格式,那么该工具包也非常方便,并且是对
jq
的补充

您可能还需要检查一个名为的强大工具。这是一张与原始海报相似的海报。您还可以从
visidata

生成脚本,Miller()非常简单。使用此input.csv文件

name,age,gender
john,20,male
jane,30,female
bob,25,male
跑步

mlr --c2j --jlistwrap cat input.csv
你会有

[
{ "name": "john", "age": 20, "gender": "male" }
,{ "name": "jane", "age": 30, "gender": "female" }
,{ "name": "bob", "age": 25, "gender": "male" }
]
这是一个相当简单的jq“一行程序”版本,它适用于“合理”大小的文件,对于非常大的文件,您需要一个不使用slurp的版本。我对jq还相当陌生,我相信有更好的方法可以做到这一点(可能只是增加一个索引值,而不是存储在数据中)。如果要使“拆分”更简短、更难阅读,可以将其替换为“./”\n”和“./”,”。注意:如果您确实需要逗号后的空格,可以在“,”上拆分,或在逗号拆分后添加|映射(gsub(“^\s+$”),以修剪前导和尾随空格

jq -Rs 'split("\n")|map(split(",")|to_entries)|.[0] as $header|.[1:]|map(reduce .[] as $item ({};.[$header[$item.key].value]=$item.value))'
以下是一个注释版本:

# jq -Rs
split("\n") | map( split(",") | to_entries ) # split lines, split comma & number
  | .[0] as $header # save [0]
  | .[1:] # and then drop it
  | map( reduce .[] as $item ( {}; .[$header[$item.key].value] = $item.value ) )
最上面的部分非常简单:在换行符上拆分数据,然后对每个元素使用逗号拆分,然后使用to_项将每个元素转换为键/值项,并使用键编号(0..N):{key:#,value:string}

然后,它使用map/reduce获取每个元素,并将其替换为一个键/值对对象,使用编号键将其索引回标题中以获取标签。对于那些新加入reduce(像我一样)的人来说,分号之前的第一个元素是初始化“累加器”(每次通过元素时都要修改的内容),因此,[…]是修改累加器,$item是我们操作的对象

更新:我现在有一个更好的版本,它不使用slurp,我们也不使用-n选项,因为它会特别处理第一行:

jq -R 'split(",") as $h|reduce inputs as $in ([]; . += [$in|split(",")|. as $a|reduce range(0,length) as $i ({};.[$h[$i]]=$a[$i])])'

你要求做的事毫无意义
jq
是一种将json作为输入并生成输出的工具。csv不是json。你不能期望这个工具能够处理这些,它不是为什么而制造的。您需要使用能够处理csv的工具或其他脚本语言,而不是试图强制执行
jq
,这可以通过一个几乎微不足道的shell/sed脚本来完成,例如,基于我是OP引用的文章的作者。虽然我提供的示例对于quick-n-dirty jq项目很有用,它不是很健壮。有一些很好的工具可以用来处理CSV,我推荐其中一个,例如:(像jq for CSV)或者这个NPM包或者这个gem,我只想眨眼就可以了,但是最近需要在评论中写更多的东西。谢谢你在这方面的努力!诚然,这更多的是一个理论问题,而不是实际问题。我最终在bash中完成了这项工作,但一直在思考是否只能在jq中完成,所以我问。以上是接近的。它输出<代码>[
# jq -Rs
split("\n") | map( split(",") | to_entries ) # split lines, split comma & number
  | .[0] as $header # save [0]
  | .[1:] # and then drop it
  | map( reduce .[] as $item ( {}; .[$header[$item.key].value] = $item.value ) )
jq -R 'split(",") as $h|reduce inputs as $in ([]; . += [$in|split(",")|. as $a|reduce range(0,length) as $i ({};.[$h[$i]]=$a[$i])])'