使用 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,您的正则表达式将是“.”,它匹配所有内容。与上面的答案不同,该脚本将处理嵌入数组 falsenull 值,并且可以选择将空数组和对象([])视为叶节点。

$ 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】:

这是一个使用 tostreamselectjoinreducesetpath

  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 文档上出现“叶元素”的路径,即没有子元素的元素,例如数字、字符串和布尔值。我们将该流通过管道传输到具有keyvalue 属性的对象中,其中key 包含路径数组的元素作为由点连接的字符串,value 包含该路径处的元素。最后,我们将整个东西放入一个数组中并在其上运行from_entries,它将key, value 对象数组转换为包含这些键值对的对象。

【讨论】:

当 JSON 包含数组时,此解决方案不起作用。例如:"a":"b":[1] 引发错误:jq: error (at :1): string (".") and number (0) cannot be added 很好的答案,尽管这会过滤掉任何评估为false 的值,即falsenull 等。这是因为leaf_pathspaths(scalars) 的简写,虽然scalars 确实选择了那些,paths 只返回它们不为假的条目。长话短说,将 leaf_paths 替换为 paths(type != "object" and type != "array") 以包含所有内容。 修复错误jq: error (at :1): string (".") and number (0) cannot be added [leaf_paths as $path | "key": [$path[] | tostring] | join("."), "value": getpath($path)] | from_entries跨度>

以上是关于使用 jq 展平嵌套的 JSON的主要内容,如果未能解决你的问题,请参考以下文章

使用 jq 为 JSON 对象的嵌套数组中的属性展平数组

使用 jq 保留键名展平 JSON

使用 Jq 展平 JSON,并在输出中使用数组索引

使用 JQ 展平分层 JSON 数组

如何使用 jq 展平复杂的 json 结构?

通过按键过滤数组,使用 jq 展平 JSON 文档