Home > database >  JSON processing with jq -- how to do complex awk-like output formatting?
JSON processing with jq -- how to do complex awk-like output formatting?

Time:07-22

I have the following JSON object:

{
    "attacks": {
        "normal fist": {
            "type": "melee",
            "damage": 3
        },
        "thunder fist": {
            "type": "melee",
            "damage": 5,
            "attributes": [ "electrical" ]
        }
    }
}

Using jq, I want to print the following to standard output:

  attacks:
  - name: normal fist
    type: melee
    damage: 3
  - name: thunder fist
    damage: 5
    attributes:
      electrical

This short script does most of what I want:

jq -r '.attacks | to_entries | .[] |
    { 
        "- name":.key,
        "  type":.value.Type, 
        "  damage":.value.Number,
    }' | awk -F: '/  "/ { gsub(/^  /,""); gsub(/"/,""); print $0 }'

There are two problems I am facing.

#1 - I only want the "attributes" key to be printed under the "thunder fist" entry. I can't figure out how to make the printing of that portion conditional.

#2 - I can't figure out how to format the nested object "attributes."

I can't find much in the jq documentation on formatting output. I'm trying to treat jq as a JSON-specific awk -- is this even possible?

CodePudding user response:

jq is Turing-complete, so if your format is well-defined, it would be possible to use jq to perform the transformation. But rather than inventing a new format, why not consider YAML:

$ gojq --yaml-output . attacks.json
attacks:
  normal fist:
    damage: 3
    type: melee
  thunder fist:
    attributes:
      - electrical
    damage: 5
    type: melee

If you really need the "-name:" prefix as shown, you could perhaps post-process the above, e.g.:

$ gojq --yaml-output . attacks.json | sed '/^  [^ ].*:$/ {s/  /  - name: /; s/:$//;}'
attacks:
  - name: normal fist
    damage: 3
    type: melee
  - name: thunder fist
    attributes:
      - electrical
    damage: 5
    type: melee

CodePudding user response:

The following does the job in this particular case, but might need fortifying for more genericity:

def indentArray(indent):
  (indent * " ") as $i
  | .[] | "\($i)\(.)";

def simplekv(indent):
  (indent * " ") as $i
  | keys_unsorted[] as $k
  | if .[$k] | type == "array"
    then "\($i)\($k):", (.[$k] | indentArray(indent 2))
    else "\($i)\($k): \(.[$k])"
    end;
    
keys_unsorted[] as $k
| ($k   ":"),
  (.[$k] as $o
  | ($o|keys_unsorted[]) as $k1
  | ("-name: "   $k1), ($o[$k1] | simplekv(2)) )
  • Related