如何使用jq将任意简单的JSON转换为CSV?

使用jq,如何将浅层对象数组的任意JSON编码转换为CSV?


这个站点上有很多问答,涵盖了对字段进行硬编码的特定数据模型,但是对于任何JSON,此问题的答案都应该有效,唯一的限制是它是具有标量属性的对象数组(无深度/复杂/子对象,如将它们展平是另一个问题)。结果应该包含一个标题行,给出字段名称。将优先选择保留第一个对象的字段顺序的答案,但这不是必须的。结果可以用双引号将所有单元格括起来,或者只将需要引用的单元格括起来(例如“ a,b”)。


例子

输入:


[

    {"code": "NSW", "name": "New South Wales", "level":"state", "country": "AU"},

    {"code": "AB", "name": "Alberta", "level":"province", "country": "CA"},

    {"code": "ABD", "name": "Aberdeenshire", "level":"council area", "country": "GB"},

    {"code": "AK", "name": "Alaska", "level":"state", "country": "US"}

]

可能的输出:


code,name,level,country

NSW,New South Wales,state,AU

AB,Alberta,province,CA

ABD,Aberdeenshire,council area,GB

AK,Alaska,state,US

可能的输出:


"code","name","level","country"

"NSW","New South Wales","state","AU"

"AB","Alberta","province","CA"

"ABD","Aberdeenshire","council area","GB"

"AK","Alaska","state","US"

输入:


[

    {"name": "bang", "value": "!", "level": 0},

    {"name": "letters", "value": "a,b,c", "level": 0},

    {"name": "letters", "value": "x,y,z", "level": 1},

    {"name": "bang", "value": "\"!\"", "level": 1}

]

可能的输出:


name,value,level

bang,!,0

letters,"a,b,c",0

letters,"x,y,z",1

bang,"""!""",0

可能的输出:


"name","value","level"

"bang","!","0"

"letters","a,b,c","0"

"letters","x,y,z","1"

"bang","""!""","1"


大话西游666
浏览 1081回答 3
3回答

回首忆惘然

首先,获取一个包含对象数组输入中所有不同对象属性名称的数组。这些将是CSV的列:(map(keys) | add | unique) as $cols然后,对于对象数组输入中的每个对象,将获得的列名映射到对象中的相应属性。这些将是CSV的行。map(. as $row | $cols | map($row[.])) as $rows最后,将列名放在行之前,作为CSV的标题,然后将结果行流传递到@csv过滤器。$cols, $rows[] | @csv现在都在一起了。请记住使用该-r标志将结果作为原始字符串获取:jq -r '(map(keys) | add | unique) as $cols | map(. as $row | $cols | map($row[.])) as $rows | $cols, $rows[] | @csv'

holdtom

瘦的jq -r '(.[0] | keys_unsorted) as $keys | $keys, map([.[ $keys[] ]])[] | @csv'要么:jq -r '(.[0] | keys_unsorted) as $keys | ([$keys] + map([.[ $keys[] ]])) [] | @csv'细节在旁边描述细节是棘手的,因为jq是面向流的,这意味着它对JSON数据序列(而不是单个值)进行操作。输入的JSON流将转换为某种内部类型,并通过过滤器传递,然后在程序末尾的输出流中进行编码。内部类型不是由JSON建模的,并且不以命名类型存在。最简单的方法是检查裸索引(.[])或逗号运算符的输出(可以通过调试器直接检查它,但这是基于jq的内部数据类型,而不是JSON背后的概念数据类型) 。$ jq -c'。[]'<<<'[“ a”,“ b”]'“一种”“ b”$ jq -cn'“ a”,“ b”'“一种”“ b”请注意,输出不是数组(应该是["a", "b"])。紧凑输出(该-c选项)表明,每个数组元素(或,过滤器的参数)在输出中均成为单独的对象(每个元素在单独的行上)。流就像JSON-seq一样,但是在编码时使用换行符而不是RS作为输出分隔符。因此,此内部类型在此答案中由通用术语“序列”指代,其中“流”保留用于编码的输入和输出。构造过滤器可以使用以下命令提取第一个对象的键:.[0] | keys_unsorted密钥通常会保持其原始顺序,但不能保证保留确切的顺序。因此,将需要使用它们对对象建立索引以按相同顺序获取值。如果某些对象的键顺序不同,这也可以防止将值放在错误的列中。为了将键作为第一行输出并使它们可用于索引,它们都存储在变量中。然后,管道的下一个阶段将引用此变量,并使用逗号运算符将标头添加到输出流之前。(.[0] | keys_unsorted) as $keys | $keys, ...逗号后的表达有点牵连。对象上的索引运算符可以采用字符串序列(例如"name", "value"),返回这些字符串的属性值序列。$keys是一个数组,而不是一个序列,因此[]可以将其转换为序列,$keys[]然后可以传递给 .[].[ $keys[] ]这也产生一个序列,因此使用数组构造函数将其转换为数组。[.[ $keys[] ]]该表达式将应用于单个对象。map()用于将其应用于外部数组中的所有对象:map([.[ $keys[] ]])最后,在此阶段,它被转换为序列,因此每个项目在输出中成为单独的行。map([.[ $keys[] ]])[]为什么将序列捆绑到map唯一的数组中以在外部解绑呢?map产生一个数组;.[ $keys[] ]产生一个序列。将mapfrom 应用于序列.[ $keys[] ]会产生一个值序列数组,但是由于序列不是JSON类型,因此您将获得一个包含所有值的扁平数组。["NSW","AU","state","New South Wales","AB","CA","province","Alberta","ABD","GB","council area","Aberdeenshire","AK","US","state","Alaska"]每个对象的值需要保持分开,以便它们在最终输出中成为单独的行。最后,序列通过@csv格式化程序传递。备用这些项目可以分离得较晚,而不是早期。代替使用逗号运算符获取序列(将序列作为正确的操作数传递),标头序列($keys)可以包装在数组中,并+用于附加值数组。在传递给之前,仍然需要将其转换为序列@csv。

慕的地6264312

以下过滤器稍有不同,它将确保将每个值都转换为字符串。(注意:使用jq 1.5+)# For an array of many objectsjq -f filter.jq (file)# For many objects (not within array)jq -s -f filter.jq (file)过滤: filter.jqdef tocsv($x):&nbsp; &nbsp; $x&nbsp; &nbsp; |(map(keys)&nbsp; &nbsp; &nbsp; &nbsp; |add&nbsp; &nbsp; &nbsp; &nbsp; |unique&nbsp; &nbsp; &nbsp; &nbsp; |sort&nbsp; &nbsp; ) as $cols&nbsp; &nbsp; |map(. as $row&nbsp; &nbsp; &nbsp; &nbsp; |$cols&nbsp; &nbsp; &nbsp; &nbsp; |map($row[.]|tostring)&nbsp; &nbsp; ) as $rows&nbsp; &nbsp; |$cols,$rows[]&nbsp; &nbsp; | @csv;tocsv(.)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JQuery