使用jq从2个文件更新嵌套值

时间:2017-10-13 11:56:46

标签: json merge jq

我已经使用jq进行了一些解析任务,几天前刚刚开始使用它并且正在享受它的多功能性,但我在下面概述的一项任务有点复杂而且我和#39;我在这个问题上与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,更新现有属性对(如果存在),或将它们添加为自己的块(如图所示,使用" NEW"标记),如果不存在的话。

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

谢谢!

3 个答案:

答案 0 :(得分:0)

这是一个简洁明了的解决方案,基于以下事实:在jq中,如果X和Y是两个JSON对象,则表达式License: 20-214767 (Validity: 21/05/2022)20C-214769 (Validity: 21/05/2022)21-214768 (Validity: 21/05/2022) 优先于Y中的键。

首先,假设以下是combine.jq:

X + Y

以下调用将产生所需的结果:

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

map( lookup(.type) + . )

一衬垫

如果你想要“一线”:

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

答案 1 :(得分:0)

另一个 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"
    }
  }
]

答案 2 :(得分:0)

这是一个解决方案,使用reduce从文件A创建临时查找表以及map来生成请求的输出,包括" NEW"文件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中的任何键(在注释中请求)。

  (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!