skip to Main Content

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" } } ] } ] 

3

Answers


  1. Chosen as BEST ANSWER

    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 )'
    

  2. 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

    Login or Signup to reply.
  3. 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
                }
              }
            }
          }
        ]
      }
    ]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search