1

I have a json file input.json thus:

{
   "foo":{
       "prefix":"abc",
       "body":[1,2,3]
   },
   "bar":{
       "prefix":"def",
       "body":[4,5,6]    
   }
}

I would like to sort it by the outer object names, with "bar" coming before "foo" in alphabetical order like so:

{
   "bar":{
       "prefix":"def",
       "body":[4,5,6]    
   },
   "foo":{
       "prefix":"abc",
       "body":[1,2,3]
   }
}

to produce file output.json.

Versions of this question have been asked of Java/Javascript (here and here)

Is there a way to accomplish this using a command line tool like sed/awk or boost.json?

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
Tryer
  • 3,580
  • 1
  • 26
  • 49
  • Objects in JS/JSON are unordered bags of key-value pairs, the property order has no inherent meaning. Why is the order important to you? – knittl Feb 14 '23 at 16:30
  • @knittl (As always, things are quite complicated.) I have VSCode snippets as json files where the trigger keywords are names of outermost objects in this file. Visual Studio IDE snippets are not json files. They are stored in a different format with the trigger keywords themselves being file names allowing one to sort by their filename in Windows explorer. I am trying to match the two sets of snippets. I am not sure there is any other way to do this but sort the json file. – Tryer Feb 14 '23 at 16:35

2 Answers2

2

Using jq, you could use the keys built-in to get the key names in sorted order and form the corresponding value object

jq 'keys[] as $k | { ($k) : .[$k] }' json

Note that jq does have a field --sort-keys option, which cannot be used here, as it internally sorts the inner level objects as well.

Inian
  • 80,270
  • 14
  • 142
  • 161
  • This does work for me. TY! The output prints `4`, `5`, `6`, etc., on separate lines. Is there a way to make the output exactly match the input file (in terms of total number of lines in the files) with only the specific lines of texts interchanged? – Tryer Feb 14 '23 at 16:43
  • 1
    @Tryer jq does a default JSON formatting of the input, which unfortunately it doesn't allow to control for specific fields – Inian Feb 14 '23 at 16:48
  • 1
    re reformatting: see e.g https://stackoverflow.com/questions/75428312/custom-json-output-formatting-with-jq – peak Feb 14 '23 at 17:17
  • 1
    To produce the desired output as given in the Q, you'd have to adapt this approach along the lines of: `[keys[] as $k | {($k): .[$k]} ] | add` – peak Feb 15 '23 at 02:23
  • @peak yes indeed. I had to modify the suggested answer based on your correction to exactly get the output as I wanted in the OP. Your method also works perfectly. Hope Inian corrects his answer. – Tryer Feb 15 '23 at 04:44
1

Here's a variable-free jq solution:

to_entries | sort_by(.key) | from_entries

It is also worth noting that gojq, the Go implementation of jq, currently always sorts the keys within all JSON objects.

peak
  • 105,803
  • 17
  • 152
  • 177
  • Just to make sure I understand your answer -- should this be `jq to_entries...` instead of just `to_entries...` ? I had to install `jq` on my machine. It is not clear in your answer whether the utility itself is `jq` or some other utility. – Tryer Feb 15 '23 at 01:55
  • 1
    @Tryer Yes, it's `jq 'to_entries | sort_by(.key) | from_entries' input.json` – pmf Feb 15 '23 at 08:40