json - 使用 jq 解析两个列表中存在的键(即使其中一个列表中可能不存在)

标签 json bash jq

(很难想出一个总结问题的标题,所以请随意改进它)。

我有一个包含以下内容的 JSON 文件:

{
    "Items": [
        {
            "ID": {
                "S": "ID_Complete"
            }, 
            "oldProperties": {
                "L": [
                    {
                        "S": "[property_A : value_A_old]"
                    }, 
                    {
                        "S": "[property_B : value_B_old]"
                    }
                ]
            },
            "newProperties": {
                "L": [
                    {
                        "S": "[property_A : value_A_new]"
                    }, 
                    {
                        "S": "[property_B : value_B_new]"
                    }
                ]
            }
        }, 
        {
            "ID": {
                "S": "ID_Incomplete"
            }, 
            "oldProperties": {
                "L": [
                    {
                        "S": "[property_B : value_B_old]"
                    }
                ]
            },
            "newProperties": {
                "L": [
                    {
                        "S": "[property_A : value_A_new]"
                    }, 
                    {
                        "S": "[property_B : value_B_new]"
                    }
                ]
            }
        }
    ]
}

我想使用 jq 以这样一种方式操作数据,即 Items[] 中的每个项目都有一个 new property_A 的值(在 newProperties 列表下)生成具有相应 id 的输出, oldnew(请参阅下面的所需输出)字段,而不管该属性在 oldProperties 列表中的值。此外,如果 oldProperties 中不存在 property_A,我仍然需要在 old 字段中填充一个null(或任何固定的字符串)。

期望的输出:

{
  "id": "id_Complete",
  "old": "[property_A : value_A_old]",
  "new": "[property_A : value_A_new]"
}
{
  "id": "ID_Incomplete",
  "old": null,
  "new": "[property_A : value_A_new]"
}

注意:即使 property_A 不存在于 oldProperties 列表中,其他属性可能(和将) 存在。

我面临的问题是,当 oldProperties 列表中不存在所需的属性时,我无法获得输出。我当前的 jq 命令如下所示:

jq -r '.Items[] | 
    { id:.ID.S, 
      old:.oldProperties.L[].S | select(. | contains("property_A")),
      new:.newProperties.L[].S | select(. | contains("property_A")) }'

它仅呈现 ID_Complete 情况,而我还需要其他情况。

有什么方法可以使用这个工具来实现吗?

提前致谢。

最佳答案

您的属性列表似乎是某个对象的值。您可以将它们映射到一个对象中,然后比较这些对象,然后报告结果。

你可以这样做:

def make_object_from_properties:
      [.L[].S | capture("\\[(?<key>\\w+) : (?<value>\\w+)\\]")]
    | from_entries
    ;
def diff_objects($old; $new):
      def _prop($key): select(has($key))[$key];
      ([($old | keys[]), ($new | keys[])] | unique) as $keys
    | [   $keys[] as $k
        | ({ value: $old | _prop($k) } // { none: true }) as $o
        | ({ value: $new | _prop($k) } // { none: true }) as $n
        | (if   $o.none                 then "add"
          elif  $n.none                 then "remove"
          elif  $o.value != $n.value    then "change"
                                        else "same"
          end) as $s
        | { key: $k, status: $s, old: $o.value, new: $n.value }
      ]
  ;
def diff_properties:
      (.oldProperties | make_object_from_properties) as $old
    | (.newProperties | make_object_from_properties) as $new
    | diff_objects($old; $new) as $diff
    | foreach $diff[] as $d ({ id: .ID.S };
          select($d.status != "same")
        | .old = ((select(any("remove", "change"; . == $d.status)) | "[\($d.key) : \($d.old)]") // null)
        | .new = ((select(any("add", "change";    . == $d.status)) | "[\($d.key) : \($d.new)]") // null)
      )
    ;
[.Items[] | diff_properties]

这会产生以下输出:

[
  {
    "id": "ID_Complete",
    "old": "[property_A : value_A_old]",
    "new": "[property_A : value_A_new]"
  },
  {
    "id": "ID_Complete",
    "old": "[property_B : value_B_old]",
    "new": "[property_B : value_B_new]"
  },
  {
    "id": "ID_Incomplete",
    "old": null,
    "new": "[property_A : value_A_new]"
  },
  {
    "id": "ID_Incomplete",
    "old": "[property_B : value_B_old]",
    "new": "[property_B : value_B_new]"
  }
]

您的数据似乎也是某种编码格式。为了获得更健壮的解决方案,您应该考虑定义一些函数来解码它们。考虑找到的方法 here关于如何做到这一点。

关于json - 使用 jq 解析两个列表中存在的键(即使其中一个列表中可能不存在),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46671563/

相关文章:

bash - 在脚本中键入 "end"以结束已启动的进程

arrays - 用jq从数组中删除空白字符串?

java - 使用java通过socket接收python json

java - 使用 GSON 将扁平化 JSON 键反序列化为正确的对象

json - 在许多 div + Struts2 中加载 JSON 的一部分

bash - 从 Bash 中的自定义选项读取参数

javascript - 通过 UI 过滤 JSON 对象数组

bash - 为什么 wget 给我两个不同的总下载时间?

python - 使用JQ从CloudFormation.template.json中删除 block

json - 在 dos/windows 中隐藏 jq 的状态输出