Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 608
Yogi Avatar asked Apr 10 '26 18:04

Yogi


2 Answers

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)"
  )                                                              
'
like image 108
pmf Avatar answered Apr 13 '26 10:04

pmf


My take is this:

. as $data
| [ path(.. | scalars) ]
| map( . as $p | { prefix: $p[0:-1]|join("."), key: $p[-1], value: $data | getpath($p) })
| group_by(.prefix)[]
| "[\(first.prefix)]", (.[] | "\(.key)=\(.value)")

Basically:

  • find all scalars,
  • get the paths to these scalars (like ["outer1","outer2","outer3","key1"])
  • create intermediate representation of the path splitted into a prefix like outer1.outer2.outer3), a key (key1) and the actual value (value1)
  • group these using the prefix
  • format
like image 37
A.H. Avatar answered Apr 13 '26 09:04

A.H.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!