使用 jq 展平嵌套的 JSON
Posted
技术标签:
【中文标题】使用 jq 展平嵌套的 JSON【英文标题】:Flatten nested JSON using jq 【发布时间】:2016-09-29 04:07:45 【问题描述】:我想展平嵌套的 json 对象,例如"a":"b":1
到 "a.b":1
以便在 solr 中消化它。
我有 11 TB 的 json 文件,它们都是嵌套的,并且在字段名称中包含点,这意味着既不是 elasticsearch(点)也不是 solr(嵌套没有_childDocument_
符号)可以按原样消化它。
其他解决方案是将字段名称中的点替换为下划线并将其推送到 elasticsearch,但我对 solr 的经验要好得多,因此我更喜欢 flatten 解决方案(除非 solr 可以按原样消化那些嵌套的 json ??) .
只有在消化过程比 solr 花费的时间少得多的情况下,我才会更喜欢 elasticsearch,因为我的首要任务是尽可能快地消化(因此我选择了 jq 而不是在 python 中编写脚本)。
请帮助。
编辑:
我认为示例 3 和 4 为我解决了这个问题: https://lucidworks.com/blog/2014/08/12/indexing-custom-json-data/
我会尽快尝试。
【问题讨论】:
【参考方案1】:我最近编写了一个名为jqg 的脚本,它可以将任意复杂的 JSON 展平并使用正则表达式搜索结果;要简单地展平 JSON,您的正则表达式将是“.
”,它匹配所有内容。与上面的答案不同,该脚本将处理嵌入数组 false
和 null
值,并且可以选择将空数组和对象([]
和 )视为叶节点。
$ jq . test/odd-values.json
"one":
"start-string": "foo",
"null-value": null,
"integer-number": 101
,
"two": [
"two-a":
"non-integer-number": 101.75,
"number-zero": 0
,
"true-boolean": true,
"two-b":
"false-boolean": false
],
"three":
"empty-string": "",
"empty-object": ,
"empty-array": []
,
"end-string": "bar"
$ jqg . test/odd-values.json
"one.start-string": "foo",
"one.null-value": null,
"one.integer-number": 101,
"two.0.two-a.non-integer-number": 101.75,
"two.0.two-a.number-zero": 0,
"two.0.true-boolean": true,
"two.0.two-b.false-boolean": false,
"three.empty-string": "",
"three.empty-object": ,
"three.empty-array": [],
"end-string": "bar"
jqg
使用 jq 1.6 进行了测试
注意:我是jqg
脚本的作者。
【讨论】:
【参考方案2】:这是一个使用 tostream、select、join、reduce 和 setpath
reduce ( tostream | select(length==2) | .[0] |= [join(".")] ) as [$p,$v] (
; setpath($p; $v)
)
【讨论】:
【参考方案3】:这只是圣地亚哥的 jq 的变体:
. as $in
| reduce leaf_paths as $path (;
. + ($path | map(tostring) | join(".")): $in | getpath($path) )
它避免了键/值构造和销毁的开销。
(如果您可以访问 jq 1.5 之后的 jq 版本,则可以省略“map(tostring)”。)
关于这两个 jq 解决方案的两个要点:
数组也被展平。
例如。给定"a": "b": [0,1,2]
作为输入,输出将是:
"a.b.0": 0,
"a.b.1": 1,
"a.b.2": 2
如果原始 JSON 中的任何键包含句点,则可能发生键冲突;这种冲突通常会导致价值的损失。例如,如果输入以下内容,就会发生这种情况:
"a.b":0, "a": "b": 1
【讨论】:
@SteveAmerige - 答案已更新,可与 jq 1.4 及更高版本一起使用。【参考方案4】:事实证明,curl -XPOST 'http://localhost:8983/solr/flat/update/json/docs' -d @json_file
就是这样做的:
"a.b":[1],
"id":"24e3e780-3a9e-4fa7-9159-fc5294e803cd",
"_version_":1535841499921514496
编辑 1:solr 6.0.1 与 bin/solr -e cloud
。集合名称是flat
,其余都是默认的(data-driven-schema
也是默认的)。
编辑 2:我使用的最终脚本:find . -name '*.json' -exec curl -XPOST 'http://localhost:8983/solr/collection1/update/json/docs' -d @ \;
。
编辑 3:也可以与 xargs 并行并使用 jq 添加 id 字段:find . -name '*.json' -print0 | xargs -0 -n 1 -P 8 -I sh -c "cat | jq '. + id: .a.b' | curl -XPOST 'http://localhost:8983/solr/collection/update/json/docs' -d @-"
其中-P
是并行度因子。我使用 jq 设置了一个 id,因此同一文档的多次上传不会在集合中创建重复项(当我搜索 -P
的最佳值时,它会在集合中创建重复项)
【讨论】:
【参考方案5】:您还可以使用以下 jq 命令以这种方式展平嵌套的 JSON 对象:
[leaf_paths as $path | "key": $path | join("."), "value": getpath($path)] | from_entries
它的工作方式是:leaf_paths
返回一个数组流,表示给定 JSON 文档上出现“叶元素”的路径,即没有子元素的元素,例如数字、字符串和布尔值。我们将该流通过管道传输到具有key
和value
属性的对象中,其中key
包含路径数组的元素作为由点连接的字符串,value
包含该路径处的元素。最后,我们将整个东西放入一个数组中并在其上运行from_entries
,它将key, value
对象数组转换为包含这些键值对的对象。
【讨论】:
当 JSON 包含数组时,此解决方案不起作用。例如:"a":"b":[1]
引发错误:jq: error (at false
的值,即false
、null
等。这是因为leaf_paths
是paths(scalars)
的简写,虽然scalars
确实选择了那些,paths
只返回它们不为假的条目。长话短说,将 leaf_paths
替换为 paths(type != "object" and type != "array")
以包含所有内容。
修复错误jq: error (at [leaf_paths as $path | "key": [$path[] | tostring] | join("."), "value": getpath($path)] | from_entries
跨度>
以上是关于使用 jq 展平嵌套的 JSON的主要内容,如果未能解决你的问题,请参考以下文章