2

I have a scenario where I already have a json file which I want to open and edit using jq.

json file(temp.json) :

{
  "a":{
    "keya" : "abc",
    "keyb" : "xyz"
  },
  "c":{
    "keyc" : "yyy"
  }
}

here I want to append another json object just before c block by searching, basically appending the value just before last object, in this case last object will always be c

  "b":{
    "keyb" : "yop"
  }

I achieved inserting the block using jq but not at the desired position. jq '. |= . + {"b":{ "keyb" : "yop" }}' temp.json

any help would be great

3
  • You know that the ordering of keys in an object is not really important in a JSON object? If the top-level object had been an array, then it would be easy to insert elements before the last element in the array. Also, is the data (the two keys and the value) always static, or is it provided via shell variables?
    – Kusalananda
    Jan 17, 2022 at 14:05
  • If you want to sort the top-level object's keys, do you still want to output the c key last, even if the added thing has key d, or something else that sorts after c?
    – Kusalananda
    Jan 17, 2022 at 14:23
  • @they yeah I would want always c to come at last even if there is a value of d there
    – Jenifer
    Jan 17, 2022 at 16:08

2 Answers 2

2

You could use jq's -S option to sort the keys. e.g.

$ jq -S '. |= . + {"b":{ "keyb" : "yop" }}' temp.json
{
  "a": {
    "keya": "abc",
    "keyb": "xyz"
  },
  "b": {
    "keyb": "yop"
  },
  "c": {
    "keyc": "yyy"
  }
}
4
  • Note that adding an object with a key sorting after c would add that key after c, not before as requested in the question.
    – Kusalananda
    Jan 17, 2022 at 15:56
  • @cas if I sort the keys using -S will it sort it alphabetically, also how to I save that back to the original file ?
    – Jenifer
    Jan 17, 2022 at 16:12
  • @Jenifer the jq man page doesn't say what kind of sorting the -S option does but it does mention that jq keys sorts the keys "alphabetically", by unicode codepoint order. I assume that -S uses the same sort order. To overwrite the original, redirect to a temporary file and rename that over the original if there were no errors - e.g. jq ... > newfile.json && mv newfile.json temp.json
    – cas
    Jan 18, 2022 at 1:18
  • @Kusalananda the OP hadn't clarified that when I wrote this answer, and the order of keys in a json file only really matters to any humans reading the file, not to software processing it - sorting seemed the obvious way to get close to what they wanted with minimal effort.
    – cas
    Jan 18, 2022 at 1:23
0

An object is an unsorted collection of keys. The ordering of keys really should not matter to an application reading the JSON document. If you want ordered data, then consider using arrays instead.

The following is a step-by-step solution. The final code is at the end.

Since arrays are ordered, we may turn our top-level object into an ordered array of keys and values using to_entries:

$ jq 'to_entries' file
[
  {
    "key": "a",
    "value": {
      "keya": "abc",
      "keyb": "xyz"
    }
  },
  {
    "key": "c",
    "value": {
      "keyc": "yyy"
    }
  }
]

We are able to insert things before the last element in this array. Here, I'm re-writing the array of "entries" using a slice of the first elements (all but the last original elements, .[:-1]), the new element, and finally the original last element (.[-1]).

$ jq 'to_entries | . |= .[:-1] + [ { key: "b", value: { keyb: "yop" } }, .[-1] ]' file
[
  {
    "key": "a",
    "value": {
      "keya": "abc",
      "keyb": "xyz"
    }
  },
  {
    "key": "b",
    "value": {
      "keyb": "yop"
    }
  },
  {
    "key": "c",
    "value": {
      "keyc": "yyy"
    }
  }
]

We may then convert this back to its original form, hoping that from_entries does not change the order of the keys:

$ jq 'to_entries | . |= .[:-1] + [ { key: "b", value: { keyb: "yop" } }, .[-1] ] | from_entries' file
{
  "a": {
    "keya": "abc",
    "keyb": "xyz"
  },
  "b": {
    "keyb": "yop"
  },
  "c": {
    "keyc": "yyy"
  }
}

Taking the new element as a key and a JSON object on the command line:

$ jq --arg key 'yellow' --argjson value '{ "type": "color" }' 'to_entries | . |= .[:-1] + [ { key: $key, value: $value }, .[-1] ] | from_entries' file
{
  "a": {
    "keya": "abc",
    "keyb": "xyz"
  },
  "yellow": {
    "type": "color"
  },
  "c": {
    "keyc": "yyy"
  }
}

You must log in to answer this question.

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