Home > database >  How to replace multiple values in json file using jq and returning the whole content
How to replace multiple values in json file using jq and returning the whole content

Time:01-29

I have a json like this

[{"version": 0.0,"Resources": [ {"TargetService": {"Type": "AWS::ECS::Service","Properties": {"TaskDefinition": "abc","LoadBalancerInfo": {"ContainerName": "def","ContainerPort": 8080}}}} ]}]

My attempt is to replace TaskDefinition key value from "abc" to "123" and ContainerName key value from "def" to "456 in one command and return the whole update json.

This is what i tried

echo $APP_SPEC | jq --arg TASK_DEFINITION "123" '(.[].Resources[].TargetService | select(.Properties)).TaskDefinition |=$TASK_DEFINITION')

But the substistuion is not happening properly and the value gets appended at the end of the josn as below.

Incorrect response:

[ { "version": 0, "Resources": [ { "TargetService": { "Type": "AWS::ECS::Service", "Properties": { "TaskDefinition": "abc", "LoadBalancerInfo": { "ContainerName": "container_name", "ContainerPort": 8080 } }, "TaskDefinition": "123" } } ] } ] 

CodePudding user response:

You don't need select, you can specify the path directly:

.[].Resources[].TargetService.Properties.TaskDefinition = "123"
| .[].Resources[].TargetService.Properties.LoadBalancerInfo.ContainerName = "456"

Or group by the top level property:

.[].Resources[].TargetService.Properties |= (
    .TaskDefinition |= "123"
    | .LoadBalancerInfo.ContainerName |= "456"
)

It's also possible to recursively merge the expected object into your existing object using *=:

.[].Resources[].TargetService.Properties *= {
    TaskDefinition: "123",
    LoadBalancerInfo: {
        ContainerName: "456"
    }
}

Output of all three variants:

[
  {
    "version": 0,
    "Resources": [
      {
        "TargetService": {
          "Type": "AWS::ECS::Service",
          "Properties": {
            "TaskDefinition": "123",
            "LoadBalancerInfo": {
              "ContainerName": "def",
              "ContainerPort": 456
            }
          }
        }
      }
    ]
  }
]

CodePudding user response:

Use the update operator |=

.[].Resources[].TargetService.Properties |= (
  .TaskDefinition = "123"
  | .LoadBalancerInfo.ContainerName = "456"
)

Demo


Note that select only filters its input, it does not descend into the filter criterion. So, if you only want to make the update if the .Properties field exists, use select and descend into it.

( .[].Resources[].TargetService
  | select(.Properties).Properties
) |= (
  .TaskDefinition = "123"
  | .LoadBalancerInfo.ContainerName = "456"
)

Demo


Note that the filter select(.Properties) will produce false if the content of that field, albeit existing, evaluates to null or false. If you want to consider such cases also as "existent", use has in the select filter to test for it.

( .[].Resources[].TargetService
  | select(has("Properties")).Properties
) |= (
  .TaskDefinition = "123"
  | .LoadBalancerInfo.ContainerName = "456"
)

Demo

CodePudding user response:

Both the above solutions works perfectly fine. Thanks @Knittl and @pmf. For others who are stuck with this kind of problem here is my complete command to pass two variables to jq and replace the values in one shot.

echo $APP_SPEC | jq --arg TASK_DEFINITION "$NEW_TASK_DEFINITION_ARN" --arg CONTAINER_NAME "$CONTAINER_NAME" '.[].Resources[].TargetService.Properties |= (.TaskDefinition = $TASK_DEFINITION | .LoadBalancerInfo.ContainerName = $CONTAINER_NAME )'
  • Related