jsonbashjqkey-value

Get all nested keys and values using jq


I have a json like this

{
  "outer1": {
    "outer2": {
      "outer3": {
        "key1": "value1",
        "key2": "value2"
      }
    },
    "outer4": {
      "key1": "value1",
      "key2": "value2"
    }
  }
}

I want output to be

[outer1.outer2.outer3]
key1 = value1
key2 = value2

[outer1.outer2.outer4]
key1 = value1
key2 = value2

I tried jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' test.json But its not what is what I want exactly


Solution

  • Assuming your input looks like this:

    {
      "outer1": {
        "outer2": {
          "outer3": {
            "key1": "value1",
            "key2": "value2"
          },
          "outer4": {
            "key1": "value1",
            "key2": "value2"
          }
        }
      }
    }
    

    You could use the --stream option to read the input as a stream of path-value pairs. For instance:

    jq --stream -n '
      reduce (inputs | select(has(1))) as [$path, $val] ({};
        .[$path[:-1] | join(".")][$path[-1]] = $val
      )
    '
    
    {
      "outer1.outer2.outer3": {
        "key1": "value1",
        "key2": "value2"
      },
      "outer1.outer2.outer4": {
        "key1": "value1",
        "key2": "value2"
      }
    }
    

    Next, format this JSON according to your needs. For example using to_entries for both levels:

    jq --stream -nr '
      reduce (inputs | select(has(1))) as [$path, $val] ({};
        .[$path[:-1] | join(".")][$path[-1]] = $val
      )
      | to_entries[] | "[\(.key)]", (
          .value | to_entries[] | "\(.key) = \(.value)"
        ), ""
    '
    
    [outer1.outer2.outer3]
    key1 = value1
    key2 = value2
    
    [outer1.outer2.outer4]
    key1 = value1
    key2 = value2
    
    

    Alternatively, combine both steps into a single foreach iteration with a sliding context window as state:

    jq --stream -nr '
      foreach (inputs | select(has(1))) as [$path, $val] ([];
        [last, ($path[:-1] | join("."))];
        (select(first != last) | "\n[\(last)]"), "\($path[-1]) = \($val)"
      )                                                              
    '