2

Let's say I have the following JSON file:

[
  {
    "name1": "fruits",
    "name2": "cars",
    "name3": "houses"
  }
]

I know I can use jq for renaming its values like the following:

jq '[.[] | .["newname1"] = .name1 | del(.name1)]' file

This works fine and it allows me to do changes even on more complex JSON structures... However, that's a very explicit declaration of the keys that I want to change. An alternative way of doing the same thing would be with the command:

jq 'map(with_entries(if .key == "name1" then .key = "newname1" else . end))' file 

This is a way of checking all keys with specific names inside a JSON that is an Array of Objects and changing them. I don't need to explicitly declare the name of the key in this case. However, this works only on an Array of Objects. I'd like to find a way of changing all keys inside a json like that in an recursive way. Let's say I have a JSON like the following one:

{
  "name1": "one",
  "type": "FeatureCollection",
  "features": [
    {
      "name1": "one",
      "valueA": "0",
      "valueB": "0",
      "keyB": "2",
      "keyC": "3"
    },
    {
      "name1": "two",
      "valueA": "11",
      "valueB": "21",
      "keyB": "15",
      "keyC": "20"
    }
  ]
}

My last jq command doesn't work on this JSON. Is there any way of changing all name1 keys recursively using jq? Perhaps finding a way of using recurse(.[]?;true) together with this command? Is it possible?

1 Answer 1

5
jq 'walk(if type == "object" then with_entries( if .key == "name1" then .key = "newname1" else . end ) else . end)' file

This is more or less straight from the jq manual (the section describing the walk() function). The walk() function acts like a "recursive map()" and the only thing that we need to be careful about is to check the type of the entity that we're currently dealing with.

Using walk(), you may also apply the first approach of yours:

jq 'walk(if type == "object" and has("name1") then ( .newname1 = .name1 | del(.name1) ) else . end)' file

This does however place the newname1 key last in each object rather than replace name1 in the position where it was found (if that matters).

Another approach (with jq 1.6) is to use the recursive decent operator .., selecting anything that has the key we're looking for, and then updating those objects:

jq '(.. | select(has("name1")?)) |= with_entries(if .key == "name1" then .key = "newname1" else . end)' file
3
  • Cool, in my case I'm using jq 1.5 yet... So in order for the last command to work, I need to substitute the recursive decent operator .. to recurse(.[]?;true)... After this change, it works fine... My feeling is that your last sample gives more possibilities for solving different kind of problems so I'll try to expand from it here... Thanks
    – raylight
    Jun 22, 2021 at 6:49
  • 1
    @raylight Good! I'm a bit hazy on what's new in 1.6 (which is what I'm using) as I've been mostly using this version to learn the tool.
    – Kusalananda
    Jun 22, 2021 at 6:54
  • Thanks a lot, this helped me so much after scratching my head for hours on how to filter or replace keys! Oct 12, 2023 at 20:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .