使用jq基于公共键值对合并两个JSON文件

时间:2019-11-29 12:00:44

标签: shell jq jenkins-groovy

我有2个具有以下结构的JSON文件

File1.json

{
  "Plugins": [
               {
                 "Name": "Plugin A",
                 "Installation": [
                  {
                    "Version": "1.0",
                    "Server" : "abc"
                  }
                  ]
               },
               {
                  "Name": "Plugin B",
                  "Installation": [
                   {
                     "Version": "2.0",
                     "Server" : "abc"
                   }
                   ]
               },
               {
                  "Name": "Plugin C",
                  "Installation": [
                   {
                     "Version": "2.0",
                     "Server" : "abc"
                   }
                   ]
               }
   ]
}

File2.json

{
  "Plugins": [
               {
                 "Name": "Plugin A",
                 "Installation": [
                  {
                    "Version": "1.1",
                    "Server" : "xyz"
                  }
                  ]
               },
               {
                  "Name": "Plugin B",
                  "Installation": [
                   {
                     "Version": "2.0",
                     "Server" : "xyz"
                   }
                   ]
                },
   ]
}

我想合并它们并获得这样的输出

{
  "Plugins": [
               {
                 "Name": "Plugin A",
                 "Installation": [
                  {
                    "Version": "1.0",
                    "Server" : "abc"
                  },
                  {
                    "Version": "1.1",
                    "Server" : "xyz"
                  }
                  ]
               },
               {
                  "Name": "Plugin B",
                  "Installation": [
                   {
                     "Version": "2.0",
                     "Server" : "abc"
                   },
                   {
                     "Version": "2.0",
                     "Server" : "xyz"
                   }
                   ]
               },
               {
                  "Name": "Plugin C",
                  "Installation": [
                   {
                     "Version": "2.0",
                     "Server" : "abc"
                   }
                   ]
               }
   ]
}

两个JSON文件具有完全相同的结构,但仅在文件内容方面不同。 我主要考虑使用 jq 实用程序。 Shell或jenkins-groovy脚本也可以。 任何帮助,将不胜感激!

1 个答案:

答案 0 :(得分:0)

这是一种实现方法:

def mergePlugin($plugin):
  if .[$plugin.Name]
  then .[$plugin.Name].Installation += $plugin.Installation
  else .[$plugin.Name] = $plugin
  end;

{
  "Plugins": (
    map(.Plugins)
    | add
    | reduce .[] as $plugin ({}; mergePlugin($plugin))
    | to_entries | map(.value)
  )
}

运行此:

jq -s -f mergePlugins.jq File*.json

命令输入参数说明:

  

--slurp / -s:不必为输入中的每个JSON对象运行过滤器,而是将整个输入流读入一个大数组,然后仅运行过滤器一次。

     

-f filename / --from-file filename:像awk的-f选项一样,从文件而不是从命令行读取过滤器。您还可以使用“#”发表评论。

This jqplay snippet通过提供对象列表而不是多个对象来模拟-s

解决方案的工作原理如下:jq -s '.' File*.json提供了{"Plugins": [...]}对象的列表。对[...]部分感兴趣,jq -s 'map(.Plugins)' File*.json提供了以下列表列表(每个文件一个):

[
  [
    {
      "Name": "Plugin A",
      ...
    },
    {
      "Name": "Plugin B",
      ...
    },
    {
      "Name": "Plugin C",
      ...
    }
  ],
  [
    {
      "Name": "Plugin A",
      ...
    },
    {
      "Name": "Plugin B",
      ...
    }
  ]
]

我们可以使用jq -s 'map(.Plugins) | add' File*.json折叠一层嵌套列表:

[
  {
    "Name": "Plugin A",
    ...
  },
  {
    "Name": "Plugin B",
    ...
  },
  ...
]

对于下一部分,由于我希望所有"Name": "Plugin X"彼此合并,因此我认为以"Plugin X"为键的字典/对象将是一个很好的数据结构,因为对于每个插件,如果之前或没有遇到,我都可以进行恒定时间查找。

我使用reduce构建此词典:

reduce .[] as $plugin ({}; ...some expression using '.' and '$plugin'...)

{}是此对象的初始值,$plugin是每个{"Name": "Plugin X", "Installation": [...]}的值,.是中间字典/对象,包含{{1 }}和值是类似"Plugin X"的对象。

由于if-then-else有点长,我将其移至辅助过滤器$plugin中。这种过滤器有两件事:mergePlugin.

这将产生:

$plugin

这几乎是最终结果,除了现在有可以废弃的不必要的{ "Plugin A": { "Name": "Plugin A", "Installation": [ { "Version": "1.0", "Server": "abc" }, { "Version": "1.1", "Server": "xyz" } ] }, ... } 包装器和缺少的{"Plugin A": {...}}包装器之外。

改进思路:

  • 我敢肯定您可以做些比...更聪明的事情

    {"Plugins": [...]}

    对于这最后一部分,它确实起作用。

  • 我还认为实际合并可以比if-then-else短。