如何使用JQ将任意嵌套的JSON转换为CSV-这样您就可以将其转换回来? [英] How to convert arbitrary nested JSON to CSV with jq – so you can convert it back?

查看:15
本文介绍了如何使用JQ将任意嵌套的JSON转换为CSV-这样您就可以将其转换回来?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用jq任意JSON对象数组转换为CSV,而此数组中的对象是嵌套的

StackOverflow有大量的问题/答案,其中引用了特定输入或输出字段,但我希望有一个通用的解决方案,

  1. 包括标题行
  2. 适用于任何JSON输入,包括嵌套数组+对象
  3. 允许具有其他记录中存在的键的缺失值的记录
  4. 不会硬编码任何字段名称,
  5. 允许在需要时将CSV转换回嵌套的JSON结构,并且
  6. 使用密钥路径作为标头名称(请参阅以下说明)。

点符号

很多使用json的产品(如CouchDBMongoDB、…)和库(如Lodash、…)使用允许访问嵌套属性值/子字段的语法变体,方法是将关键字片段与字符(通常为点符号)连接。

这样的键路径的一个示例是"a.b.0.c"引用此JSON代码段中的深度嵌套属性:

{
  "a": {
    "b": [
      {
        "c": 123,
      }
    ]
  }
}

注意事项:在大多数情况下,使用此方法是一种实用的解决方案,但这意味着要么必须禁止属性名称中的点字符,要么必须发明一个更复杂(且绝对从未使用过的属性名称)来转义属性名称中的点/访问嵌套字段。MongoDB简单地banned usage of "." in documents until v5.0,一些库有用于字段访问的变通方法(Lodash example)。

尽管如此,为简单起见,解决方案应该在CSV输出的头中为嵌套属性使用所描述的点语法。如果存在解决此问题的解决方案变体,例如使用JSONPath

作为输入的JSON数组示例

[
    {
        "a": {
            "b": [
                {
                    "c": 123
                }
            ]
        }
    },
    {
        "a": {
            "b": [
                {
                    "c": "foo " bar",
                    "d": "qux"
                }
            ]
        }
    },
    {
        "a": {
            "b": [
                {
                    "d": 456
                }
            ]
        }
    }
]

CSV输出示例

输出应具有包含所有字段的标头(即使第一个数组中的对象没有为所有现有键路径定义值)。

若要使输出可由人类直观地编辑,每行应表示输入数组中的一个对象。

预期输出应如下所示:

"a.b.0.c","a.b.0.d"
123,
"foo "" bar","qux"
,456

命令行

这就是我需要的:

cat example.json | jq <MISSING CODE HERE>

推荐答案

以下<[2-1]和fromcsv函数提供了对所述问题的解决方案,除了关于头的要求(6)的一个复杂性。本质上,通过添加矩阵转置步骤,可以使用此处给出的函数来满足此要求。

无论是否添加转置步骤,这里采用的方法的优点是对JSON键或值没有限制。特别是,他们可能 包含句点、换行符和/或NUL字符。

在该示例中,给出了一个对象数组,但实际上任何有效的JSON文档流都可以用作tocsv的输入;由于JQ的魔力,fromcsv将重新创建原始流(从实体到实体相等的意义上)。

当然,由于没有CSV标准,由 tocsv函数可能并非所有CSV处理器都能理解。在……里面 具体来说,请注意这里定义的tocsv函数映射 在JSON字符串中嵌入换行符或两个字符的键名称 字符串&Quot; ";(即直译反斜杠后跟字母";n";); 逆运算执行逆转换以满足 往返要求(&Q;)。

(使用tail只是为了简化表示;它将 修改解决方案以使其成为唯一的JQ解决方案非常简单。)

CSV是在假设任何值都可以是 包括在字段中,只要(A)该字段被引用,以及(B) 字段中的双引号是双引号。

任何支持往返的通用解决方案都必然是 有点复杂。这里提出的解决方案的主要原因是 比人们想象的更复杂是因为第三列是 添加,部分原因是为了更容易区分整数和 整数值字符串,但主要是因为这样做很容易 区分JQ生成的大小为1和大小为2的数组 --stream选项。不用说,还有其他方法 这些问题是可以解决的;给JQ的电话数量可以 也会减少。

该解决方案以一个测试脚本的形式呈现,该脚本检查一个有说服力的测试用例的往返需求:

#!/bin/bash

function json {
    cat<<EOF
[
  {
    "a": 1,
    "b": [
      1,
      2,
      "1"
    ],
    "c": "d",ef",
    "embed"ed": "quote",
    "null": null,
    "string": "null",
    "control characters": "au0000c",
    "newline": "a
b"
  },
  {
    "x": 1
  }
]
EOF
}

function tocsv {
 jq -ncr --stream '
   (["path", "value", "stringp"],
    (inputs | . + [.[1]|type=="string"]))
   | map( tostring|gsub(""";"""") | gsub("
"; "\n"))
   | ""(.[0])","(.[1])",(.[2])" 
'
}

function fromcsv { 
    tail -n +2 | # first duplicate backslashes and deduplicate double-quotes
    jq -rR '"[(gsub("\\";"\\") | gsub("""";"\"") ) ]"' |
    jq -c '.[2] as $s 
           | .[0] |= fromjson 
           | .[1] |= if $s then . else fromjson end 
           | if $s == null then [.[0]] else .[:-1] end
             # handle newlines
           | map(if type == "string" then gsub("\\n";"
") else . end)' |
    jq -n 'fromstream(inputs)'
}    

# Check the roundtrip:
json | tocsv | fromcsv | jq -s '.[0] == .[1]' - <(json)

以下是将由json | tocsv生成的csv,只是so似乎不允许文字为空,所以我将其替换为

"path","value",stringp
"[0,""a""]","1",false
"[0,""b"",0]","1",false
"[0,""b"",1]","2",false
"[0,""b"",2]","1",true
"[0,""b"",2]","false",null
"[0,""c""]","d"",ef",true
"[0,""embed""ed""]","quote",true
"[0,""null""]","null",false
"[0,""string""]","null",true
"[0,""control characters""]","ac",true
"[0,""newline""]","a
b",true
"[0,""newline""]","false",null
"[1,""x""]","1",false
"[1,""x""]","false",null
"[1]","false",null

这篇关于如何使用JQ将任意嵌套的JSON转换为CSV-这样您就可以将其转换回来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆