opy*_*pyh 7 csv json google-sheets mongodb jq
如何使用jq将任意JSON 对象数组转换为 CSV,而该数组中的对象是嵌套的?
\nStackOverflow 有大量的问题/答案,其中引用了特定的输入或输出字段,但我希望有一个通用的解决方案
\n许多使用 JSON 的产品(如CouchDB、MongoDB、 \xe2\x80\xa6 )和库(如Lodash、 \xe2\x80\xa6 )使用语法变体,允许通过将关键片段与字符,通常是一个点(\xe2\x80\x98dot 表示法\xe2\x80\x99)。
\n像这样的关键路径的一个示例是"a.b.0.c"引用此 JSON 片段中的深度嵌套属性:
{\n "a": {\n "b": [\n {\n "c": 123,\n }\n ]\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n警告:在大多数情况下,使用此方法是一种实用的解决方案,但这意味着必须在属性名称中禁止点字符,或者必须发明更复杂的(绝对从未使用过的属性名称)来转义属性名称中的点/访问嵌套字段。MongoDB在 v5.0 之前就禁止使用"."in 文档,一些库有字段访问的解决方法(Lodash 示例)。
尽管如此,为了简单起见,解决方案应在 CSV 输出\xe2\x80\x99s 标头中使用所描述的点语法来表示嵌套属性。如果有一个解决方案变体可以解决这个问题,例如使用JSONPath ,那就太好了。
\n[\n {\n "a": {\n "b": [\n {\n "c": 123\n }\n ]\n }\n },\n {\n "a": {\n "b": [\n {\n "c": "foo \\" bar",\n "d": "qux"\n }\n ]\n }\n },\n {\n "a": {\n "b": [\n {\n "d": 456\n }\n ]\n }\n }\n]\nRun Code Online (Sandbox Code Playgroud)\n输出应该有一个包含所有字段的标头(即使第一个数组中的对象没有为所有现有键路径定义值)。
\n为了使输出可供人类直观地编辑,每一行应代表输入数组中的一个对象。
\n预期输出应如下所示:
\n"a.b.0.c","a.b.0.d"\n123,\n"foo "" bar","qux"\n,456\nRun Code Online (Sandbox Code Playgroud)\n这就是我需要的:
\ncat example.json | jq <MISSING CODE HERE>\nRun Code Online (Sandbox Code Playgroud)\n
以下是jq将嵌套 JSON 对象数组转换为 CSV 的调用:
jq -r \'(. | map(leaf_paths) | unique) as $cols | map (. as $row | ($cols | map(. as $col | $row | getpath($col)))) as $rows | ([($cols | map(. | map(tostring) | join(".")))] + $rows) | map(@csv) | .[]\nRun Code Online (Sandbox Code Playgroud)\n尝试此解决方案的最快方法是使用 JQPlay。
\nCSV 输出将有一个标题行。它将包含输入对象中任何位置存在的所有属性,包括点表示法中的嵌套属性。每个输入数组元素将表示为单行,缺少的属性将表示为空 CSV 字段。
\nbash在或类似的 shell中使用解决方案 1创建 JSON 输入文件\xe2\x80\xa6
\necho \'[{"a": {"b": [{"c": 123}]}},{"a": {"b": [{"c": "foo \\" bar","d": "qux"}]}},{"a": {"b": [{"d": 456}]}}]\' > example.json\nRun Code Online (Sandbox Code Playgroud)\n然后使用此 jq 命令在标准输出上输出 CSV:
\ncat example.json | jq -r \'(. | map(leaf_paths) | unique) as $cols | map (. as $row | ($cols | map(. as $col | $row | getpath($col)))) as $rows | ([($cols | map(. | map(tostring) | join(".")))] + $rows) | map(@csv) | .[]\'\nRun Code Online (Sandbox Code Playgroud)\n\xe2\x80\xa6 或将输出写入example.csv:
cat example.json | jq -r \'(. | map(leaf_paths) | unique) as $cols | map (. as $row | ($cols | map(. as $col | $row | getpath($col)))) as $rows | ([($cols | map(. | map(tostring) | join(".")))] + $rows) | map(@csv) | .[]\' > example.csv\nRun Code Online (Sandbox Code Playgroud)\n这是一个Node.js 示例,您可以在 RunKit 上尝试。它将使用解决方案 1 中的方法生成的 CSV 转换回嵌套 JSON 对象的数组。
\n这是过滤器的更长、带注释的版本jq。
# 1) Find all unique leaf property names of all objects in the input array. Each nested property name is an array with the components of its key path, for example ["a", 0, "b"].\n(. | map(leaf_paths) | unique) as $cols |\n\n# 2) Use the found key paths to determine all (nested) property values in the given input records.\nmap (. as $row | ($cols | map(. as $col | $row | getpath($col)))) as $rows |\n\n # 3) Create the raw output array of rows. Each row is represented as an array of values, one element per existing column.\n (\n\n # 3.1) This represents the header row. Key paths are generated here.\n [($cols | map(. | map(tostring) | join(".")))]\n\n + # 3.2) concatenate the header row with all other rows\n $rows\n\n )\n\n # 4) Convert each row to a escaped CSV string.\n | map(@csv)\n\n # 5) output each array element directly. Without this, the result would be a JSON array of CSV strings.\n | .[]\nRun Code Online (Sandbox Code Playgroud)\n如果您确实需要支持属性名称中的点字符,您可以为键路径语法使用不同的分隔符字符串(将点替换为"."其他内容),或者将map(tostring) | join(".")部分替换为tostring- 这会生成一个 JSON 字符串数组,您可以将其替换为字符串。可以用作关键路径 - 不需要点符号。这是具有此解决方案变体的JQPlay。
完整jq命令:
jq -r (. | map(leaf_paths) | unique) as $cols | map (. as $row | ($cols | map(. as $col | $row | getpath($col)))) as $rows | ([($cols | map(. | tostring))] + $rows) | map(@csv) | .[]\nRun Code Online (Sandbox Code Playgroud)\n该变体的输出 CSV 看起来像这样 \xe2\x80\x93 it\xe2\x80\x99s 可读性较差,并且对于您希望人们直观地理解 CSV\xe2\x80\x99s 标头的情况没有用处:
\n"[""a"",""b"",0,""c""]","[""a"",""b"",0,""d""]"\n123,\n"foo "" bar","qux"\n,456\nRun Code Online (Sandbox Code Playgroud)\n请参阅下文,了解如何将此格式转换回您的编程语言的表示形式。
\n如果输入的嵌套属性不包含 no ".",则将 CSV 转换回 JSON 很简单,例如使用支持点表示法的库或使用JSONPath。
$.a.b.0.c或 这样的选择器$[\'a\'][\'b\'][0][\'c\']来设置每个记录的每个嵌套属性。解决方案 2(使用 JSON 数组作为标头)允许您将标头解释为 JSON 数组字符串。然后,您可以从每个标头生成 JSON 路径,并重新创建所有记录/对象:
\n"[""a"",""b"",0,""c""]"(CSV)
\xe2\x86\x92 ["a","b",0,"c"](取消转义并解析为 JSON 后的键路径组件数组)
\xe2\x86\x92 $.["a"]["b"][0]["c"](JSONPath)
\xe2\x86\x92 { a: { b: [{c: \xe2\x80\xa6 }] } }(嵌套重新生成的对象)
我编写了一个示例 Node.js 脚本来将这样的 CSV 转换回 JSON。您可以在 RunKit 中尝试解决方案 2。
\n| 归档时间: |
|
| 查看次数: |
4484 次 |
| 最近记录: |