Modifying array of key value in JSON jq
Asked Answered
P

3

8

In case, I have an original json look like the following:

{
  "taskDefinition": {
    "containerDefinitions": [
      {
        "name": "web",
        "image": "my-image",
        "environment": [
          {
            "name": "DB_HOST",
            "value": "localhost"
          },
          {
            "name": "DB_USERNAME",
            "value": "user"
          }
        ]
      }
    ]
  }
}

And I would like to inplace modify the value for the matched key like so:

jq '.taskDefinition.containerDefinitions[0].environment[] | select(.name=="DB_USERNAME") | .value="new"' json

I got the output

{
  "name": "DB_USERNAME",
  "value": "new"
}

But I want more like in-place modify or the whole json from the original with new value modified, like this:

{
      "taskDefinition": {
        "containerDefinitions": [
          {
            "name": "web",
            "image": "my-image",
            "environment": [
              {
                "name": "DB_HOST",
                "value": "localhost"
              },
              {
                "name": "DB_USERNAME",
                "value": "new"
              }
            ]
          }
        ]
      }
    }

Is it possible to do with jq or any known workaround?

Thank you.

Updated

For anyone looking for editing multi-values, here is the approach I use

JQ=""
for e in DB_HOST=rds DB_USERNAME=xxx; do
    k=${e%=*}
    v=${e##*=}
    JQ+="(.taskDefinition.containerDefinitions[0].environment[] | select(.name==\"$k\") | .value) |= \"$v\" | "
done

jq '${JQ%??}' json

I think there should be more concise way, but this seems working fine.

Paunch answered 4/1, 2019 at 12:46 Comment(0)
O
7

It is enough to assign to the path, if you are using |=, e.g.

jq '
  (.taskDefinition.containerDefinitions[0].environment[] | 
   select(.name=="DB_USERNAME") | .value) |= "new"
' infile.json

Output:

{
  "taskDefinition": {
    "containerDefinitions": [
      {
        "name": "web",
        "image": "my-image",
        "environment": [
          {
            "name": "DB_HOST",
            "value": "localhost"
          },
          {
            "name": "DB_USERNAME",
            "value": "new"
          }
        ]
      }
    ]
  }
}
Oldham answered 4/1, 2019 at 14:51 Comment(1)
Thanks. This solution looks the most obvious to the question I had. I also do append for multi values modify based on your answer, please see the updated section in the answer.Paunch
S
4

Here is a select-free solution using |=:

.taskDefinition.containerDefinitions[0].environment |=
  map(if .name=="DB_USERNAME" then .value = "new"
      else . end)

Avoiding select within the expression on the LHS of |= makes the solution more robust w.r.t. the version of jq being used.

Selfgovernment answered 4/1, 2019 at 15:20 Comment(0)
S
3

You might like to consider this alternative to using |=:

walk( if type=="object" and .name=="DB_USERNAME" 
      then .value="new" else . end)
Selfgovernment answered 4/1, 2019 at 15:5 Comment(1)
Why aren't you merging your two answers?Unguiculate

© 2022 - 2024 — McMap. All rights reserved.