使用 JQ 合并两个嵌套的 JSON 文件

Merge two nested JSON files using JQ

我正在尝试合并两个 JSON 文件。主要目标是用第二个文件中的 environment 个变量覆盖第一个文件中的 environment 个变量。

第一个文件:

{
    "containerDefinitions": [
        {
            "name": "foo",
            "image": "nginx:latest",
            "cpu": 1024,
            "memory": 4096,
            "memoryReservation": 2048,
            "portMappings": [
                {
                    "containerPort": 8080,
                    "hostPort": 0,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "SERVER_PORT",
                    "value": "8080"
                },
                {
                    "name": "DB_NAME",
                    "value": "example_db"
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/dev/ecs/example",
                    "awslogs-region": "us-west-1",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "family": "bar",
    "taskRoleArn": "arn:aws:iam::111111111111:role/assume-ecs-role",
    "executionRoleArn": "arn:aws:iam::111111111111:role/ecs-task-execution-role",
    "networkMode": "bridge",
    "volumes": [],
    "placementConstraints": [],
    "requiresCompatibilities": [
        "EC2"
    ]
}

第二个文件:

{
    "containerDefinitions": [
        {
            "environment": [
                {
                    "name": "SERVER_PORT",
                    "value": "8081"
                }
            ]
        }
    ]
}

下一个产品必须是:

{
    "containerDefinitions": [
        {
            "name": "foo",
            "image": "nginx:latest",
            "cpu": 1024,
            "memory": 4096,
            "memoryReservation": 2048,
            "portMappings": [
                {
                    "containerPort": 8080,
                    "hostPort": 0,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "SERVER_PORT",
                    "value": "8081"
                },
                {
                    "name": "DB_NAME",
                    "value": "example_db"
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/dev/ecs/example",
                    "awslogs-region": "us-west-1",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "family": "bar",
    "taskRoleArn": "arn:aws:iam::111111111111:role/assume-ecs-role",
    "executionRoleArn": "arn:aws:iam::111111111111:role/ecs-task-execution-role",
    "networkMode": "bridge",
    "volumes": [],
    "placementConstraints": [],
    "requiresCompatibilities": [
        "EC2"
    ]
}

我尝试了下一步:

jq -s 'reduce .[] as $item ({}; reduce ($item | keys_unsorted[]) as $key (.; $item[$key] as $val | ($val | type) as $type | .[$key] = if ($type == "array") then (.[$key] + $val | unique) elif ($type == "object") then (.[$key] + $val) else $val end))'  1.json 2.json

但结果是:

{
  "containerDefinitions": [
    {
      "name": "foo",
      "image": "nginx:latest",
      "cpu": 1024,
      "memory": 4096,
      "memoryReservation": 2048,
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 0,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "environment": [
        {
          "name": "SERVER_PORT",
          "value": "8080"
        },
        {
          "name": "DB_NAME",
          "value": "example_db"
        }
      ],
      "mountPoints": [],
      "volumesFrom": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/dev/ecs/example",
          "awslogs-region": "us-west-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    },
    {
      "environment": [
        {
          "name": "SERVER_PORT",
          "value": "8081"
        }
      ]
    }
  ],
  "family": "bar",
  "taskRoleArn": "arn:aws:iam::111111111111:role/assume-ecs-role",
  "executionRoleArn": "arn:aws:iam::111111111111:role/ecs-task-execution-role",
  "networkMode": "bridge",
  "volumes": [],
  "placementConstraints": [],
  "requiresCompatibilities": [
    "EC2"
  ]
}

任何人都可以帮助找出如何达到正确的结果吗?

像这样的东西就可以了:

 (input | .containerDefinitions[0].environment | from_entries) as $new_env
| input | .containerDefinitions[].environment |= ((from_entries + $new_env) | to_entries)

Online demo


如果不清楚,调用应该如下所示:

jq -n '...' 2.json 1.json

这是一个解决方案,使用 tostreamhas(1) 从第二个文件读取值,并使用 setpath 在第一个文件中设置它们:

jq 'reduce (input | tostream | select(has(1))) as $i (.; setpath($i[0]; $i[1]))' \
  1.json 2.json

Demo


以相反的顺序 (2.json 1.json) 提供文件时,上下文 .input 必须交换:

jq 'reduce (tostream | select(has(1))) as $i (input; setpath($i[0]; $i[1]))' \
  2.json 1.json

Demo