I'm trying to merge two JSON files. The main destination is to overwrite environment
variables in the 1st file with environment
variables in the 2nd.
1st file:
{
"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"
]
}
2nd file:
{
"containerDefinitions": [
{
"environment": [
{
"name": "SERVER_PORT",
"value": "8081"
}
]
}
]
}
The product has to be next:
{
"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"
]
}
I tried to do next:
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
But the result is:
{
"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"
]
}
Could anyone help to find out how to reach the right result?
CodePudding user response:
Something like this will do the trick:
(input | .containerDefinitions[0].environment | from_entries) as $new_env
| input | .containerDefinitions[].environment |= ((from_entries $new_env) | to_entries)
In case it's unclear, the invocation should look like so:
jq -n '...' 2.json 1.json
CodePudding user response:
Here's a solution using tostream
and has(1)
to read the values from the second file, and setpath
to set them in the first file:
jq 'reduce (input | tostream | select(has(1))) as $i (.; setpath($i[0]; $i[1]))' \
1.json 2.json
When providing the files in reversed order (2.json 1.json
), the context .
and input
have to be swapped:
jq 'reduce (tostream | select(has(1))) as $i (input; setpath($i[0]; $i[1]))' \
2.json 1.json