Use jq to sort keys in json using one of the properties

I need to sort following JSON file by keys with natural order but keys that a listed in the 'required` section should go first, how to do that?

Following command sorts only by keys putting them in natural order:

jq --sort-keys . /tmp/source.json > ./tmp/target.json
{
   "Request": {
        "properties": {
          "capacity": {
            "$ref": "SubscriptionCapacity"
          },
          "metadata": {
            "$ref": "MeterDefinitionsAndPolicy"
          },
          "data": {
            "type": "string"
          },
          "avs": {
            "pattern": "standard:v2",
            "type": "string"
          }
        },
        "required": [
          "data",
          "avs"
        ],
        "type": "object"
      }
}

Expected output should be like:

{
   "Request": {
        "properties": {
          "avs": {
            "$ref": "Pricing"
          },
          "data": {
            "type": "string"
          }
          "capacity": {
            "$ref": "SubscriptionCapacity"
          },
          "metadata": {
            "$ref": "MeterDefinitionsAndPolicy"
          }
        },
        "required": [
          "data",
          "avs"
        ],
        "type": "object"
      }
}

Solution 1:

Here's a straightforward and fairly efficient approach that takes advantage of the fact that keys produces the key names as a sorted array:

. as $in
| .Request
| (.required|sort) as $required
| (.properties|keys) as $all
| ($all - $required) as $optional
| .properties as $p
| $in
| .Request.properties = reduce ($required[], $optional[]) as $key ({}; . + {($key): $p[$key]} )


Note that gojq, the Go implementation of jq, does support keys but does not in general respect user-specified ordering of keys within objects.

Solution 2:

You can do something along the lines of

.Request.required as $req
| .Request.properties |= (
    to_entries
    | sort_by(.key)
    | group_by(IN(.key; $req[]) | not)
    | map(from_entries)
    | add
  )
{
  "Request": {
    "properties": {
      "avs": {
        "pattern": "standard:v2",
        "type": "string"
      },
      "data": {
        "type": "string"
      },
      "capacity": {
        "$ref": "SubscriptionCapacity"
      },
      "metadata": {
        "$ref": "MeterDefinitionsAndPolicy"
      }
    },
    "required": [
      "data",
      "avs"
    ],
    "type": "object"
  }
}

Demo

but you are not guaranteed that after any follow-up processing the order will stay the same as objects typically do not have an order, arrays do.