Search code examples
jsonbashcsvexport-to-csvjq

jq "object cannot be tsv-formatted, only array" error when making table from json data


My script creates a json data set and then tries to present it as a table.

In the output for the first line of code, you can see an example data set:

echo ${conn_list[@]} | jq '.'

{
  "host": {
    "name": "mike1",
    "node": "c04",
    "s_ip": "10.244.7.235",
    "s_port": "38558",
    "d_ip": "129.12.34.567",
    "d_port": "22",
    "pif_ip": "129.23.45.678",
    "pif_port": "11019"
  }
}
{
  "host": {
    "name": "fhlb-test",
    "node": "c04",
    "s_ip": "10.244.7.20",
    "s_port": "49846",
    "d_ip": "129.98.76.543",
    "d_port": "22",
    "pif_ip": "129.87.65.432",
    "pif_port": "23698"
  }
}

I use the following jq command with @tsv to try to build and fill the table but run into this error:

echo ${conn_list[@]} | jq -r '["NAME","NODE","SOURCE IP","SOURCE PORT","DESTINATION IP","DESTINATION PORT","GATEWAY IP","GATEWAY PORT"], (.[], map(length*"-")), (.[] | [.name, .node, .s_ip, .s_port, .d_ip, .d_port, .pif_ip, .pif_port]) | @tsv'

NAME    NODE    SOURCE IP       SOURCE PORT     DESTINATION IP  DESTINATION PORT        GATEWAY IP      GATEWAY PORT
jq: error (at <stdin>:1): object ({"name":"mi...) cannot be tsv-formatted, only array
NAME    NODE    SOURCE IP       SOURCE PORT     DESTINATION IP  DESTINATION PORT        GATEWAY IP      GATEWAY PORT
jq: error (at <stdin>:1): object ({"name":"fh...) cannot be tsv-formatted, only array

My goal is to have only one column title row in the table instead of one for each entry, and of course to display the data instead of the error. The '(.[], map(length*"-"))' bit is meant to automatically generate right size dashes to separate the column titles from the data. Anyone see what I'm doing wrong? :)


Solution

  • A fixed version might look like:

    jq -rn '
    # Assign the list of fields to a variable
    ["NAME","NODE","SOURCE IP","SOURCE PORT","DESTINATION IP","DESTINATION PORT","GATEWAY IP","GATEWAY PORT"] as $fields |
    (
      $fields,                        # emit the list as a header
      ($fields | map(length*"-")),    # print separators below each header
      (inputs | .[] | [.name, .node, .s_ip, .s_port, .d_ip, .d_port, .pif_ip, .pif_port])
    ) | @tsv' <<<"$s" # where s is a string with your JSON content.
    

    ...which emits as output, for your input (without any reformatting to align the tabs):

    NAME    NODE    SOURCE IP   SOURCE PORT DESTINATION IP  DESTINATION PORT    GATEWAY IP  GATEWAY PORT
    ----    ----    ---------   ----------- --------------  ----------------    ----------  ------------
    mike1   c04 10.244.7.235    38558   129.12.34.567   22  129.23.45.678   11019
    fhlb-test   c04 10.244.7.20 49846   129.98.76.543   22  129.87.65.432   23698
    

    The immediate bug was the inclusion of .[] in (.[], map(length*"-")) -- the first part of that is pointless, and does nothing but insert your map contents (which isn't valid in TSV content, not being a list) into the data stream.