【问题标题】:Updating nested values from 2 files using jq使用 jq 从 2 个文件更新嵌套值
【发布时间】:2017-10-13 11:56:46
【问题描述】:

我已经使用 jq 完成了一些解析任务,几天前才开始使用它并且正在享受它的多功能性,但是我在下面概述的一项任务有点复杂,我被困在摔跤中用 jq 超过这个。

我有 2 个文件,它们都具有相似的数据架构。文件 A 和 B,以及我希望得到的输出,如下所示:

文件 A:

[
  {
    "type": "hive-site",
    "tag": 1507894175,
    "properties" : {
      "javax.jdo.option.ConnectionPassword" : "hortonworks1"
    }
  },
  {
    "type": "admin-properties",
    "tag": 1507894175,
    "properties": {
      "fieldA": "valueA",
      "fieldB": "valueB"
    }
  }
]

文件 B:

[
  {
    "type": "hive-site",
    "properties" : {
      "javax.jdo.option.ConnectionPassword" : "hortonworks2"
    }
  },
  {
    "type": "admin-properties",
    "properties": {
    "fieldA": "valueA",
    "fieldB": "valueB",
    "fieldC": "valueC"
    }
  },
  {
    "type": "other-type",
    "properties": {
      "newFieldA": "valueA",
      "newFieldB": "valueB"
    }
  }
]

结果:文件 C(以文件 A 为基础,对文件 B 进行修改)

[
  {
    "type": "hive-site",
    "tag": 1507894175,
    "properties" : {
      "javax.jdo.option.ConnectionPassword" : "hortonworks2"
    }
  },
  {
    "type": "admin-properties",
    "tag": 1507894175,
    "properties": {
      "fieldA": "valueA",
      "fieldB": "valueB",
      "fieldC": "valueC"
    }
  },
  {
    "type": "other-type",
    "tag": NEW,
    "properties": {
      "newFieldA": "valueA",
      "newFieldB": "valueB"
    }
  }
]

我想从文件 B 中取出“属性”下的所有对,然后将它们推送到文件 A,更新现有的属性对(如果存在),或者将它们添加为自己的块(使用“新”标签作为显示)如果他们没有。

我找到了类似的答案(herehere),但没有一个足够接近,可以根据我的目的进行修改。

谢谢!!

【问题讨论】:

    标签: json merge jq


    【解决方案1】:

    另一个jq解决方案(按.type键分组):

    jq --slurpfile f2 fileB '[$f2[0] + . | group_by(.type)[] 
         | if .[1] then .[1] + .[0] else .[0] end]' fileA
    

    输出:

    [
      {
        "type": "admin-properties",
        "tag": 1507894175,
        "properties": {
          "fieldA": "valueA",
          "fieldB": "valueB",
          "fieldC": "valueC"
        }
      },
      {
        "type": "hive-site",
        "tag": 1507894175,
        "properties": {
          "javax.jdo.option.ConnectionPassword": "hortonworks2"
        }
      },
      {
        "type": "other-type",
        "properties": {
          "newFieldA": "valueA",
          "newFieldB": "valueB"
        }
      }
    ]
    

    【讨论】:

    • 感谢您的帮助!我发现这个解决方案会将所有 fileA 属性替换为 fileB 中的属性,而不是将两者合并:/
    【解决方案2】:

    这是一个简洁明了的解决方案,基于以下事实:在 jq 中,如果 X 和 Y 是两个 JSON 对象,则表达式 X + Y 优先于 Y 中的键。

    首先,假设 combine.jq 中有以下内容:

    def lookup($t):
      $A[] | select(.type==$t) // {tag:"NEW"};
    
    map( lookup(.type) + . )
    

    随后的调用将产生所需的结果:

    jq --argfile A A.json  -f combine.jq  B.json
    

    单线

    如果您想要“单线”:

    jq --argfile A A.json 'map((.type as $t|$A[]|select(.type==$t)//{tag:"NEW"}) + .)' B.json
    

    【讨论】:

    • 示例输出中显示的“新”标签可以包含在对 lookup 的微小更改中,例如def lookup($t): $A[] | select(.type==$t) // {tag:"NEW"};
    • 嗯,我似乎无法让它工作。这是我正在尝试的:Try it online
    • @EdgarOrendain - 真是一团糟!我建议您尝试在命令行提示符下使用 jq,因为您有两个 JSON 文件。无论如何,请确保您的 JSON 确实是 JSON。请注意,如果您的 jq 是古老的,您将无法在形式函数参数中使用 $。
    • @EdgarOrendain - 我能够让您的示例在 tio 上工作(使用 jq 1.5) - 我使用了 -n 选项;使用 def A: 和 def B: 将数据添加到“代码”部分;并相应地进行了其他小的修改。
    • 但如果您想使用在线资源,我建议您使用 jqplay.org。
    【解决方案3】:

    这是一个解决方案,它使用reduce 从文件 A 和 map 创建一个临时查找表,以生成请求的输出,包括文件 B 中不存在于文件 A 中的项目上的“新”标签。

      (reduce $A[] as $a({};.[$a.type]=$a)) as $t
    | map(if $t[.type]==null then {tag:"NEW"}+. else $t[.type]*. end)
    

    Try it online!

    这可以通过消除地图中的if 并改用// Alternative operator 来更简洁。例如

      (reduce $A[] as $a({};.[$a.type]=$a)) as $t | map( ($t[.type]//{tag:"NEW"})*. )
    

    Try it online!

    示例运行(假设过滤器在filter.jq 中,数据在FileA.jsonFileB.json 中):

    $ jq -M -f filter.jq --argfile A FileA.json FileB.json
    [
      {
        "type": "hive-site",
        "tag": 1507894175,
        "properties": {
          "javax.jdo.option.ConnectionPassword": "hortonworks2"
        }
      },
      {
        "type": "admin-properties",
        "tag": 1507894175,
        "properties": {
          "fieldA": "valueA",
          "fieldB": "valueB",
          "fieldC": "valueC"
        }
      },
      {
        "tag": "NEW",
        "type": "other-type",
        "properties": {
          "newFieldA": "valueA",
          "newFieldB": "valueB"
        }
      }
    ]
    

    以下过滤器还将包括文件 A 中而不是文件 B 中的任何键(如 cmets 中所要求的)。

      (reduce $A[] as $a({};.[$a.type]=$a)) as $t  # build lookup table
    | map( ($t[.type]//{tag:"NEW"})*. )            # apply A to B
    | (($t|keys_unsorted)-map(.type)) as $o        # find keys in A not in B
    | [$t[$o[]]] + .                               # add back those objects
    

    Try it online!

    【讨论】:

    • 嘿,这个解决方案看起来很有前途,只是我还想保持文件 A 中的块不被文件 B 更新,而不是把它们扔掉。我编辑了您发布的链接 - 任何后续行动将不胜感激! (编辑:道歉,评论太长,在下一条评论中更新链接)
    • 这个版本保留了所有A:Try it online!
    猜你喜欢
    • 1970-01-01
    • 2015-04-29
    • 2017-06-09
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多