Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using jq to split a string into nested objects

Tags:

json

jq

I'm trying to export data from consul kv into json with a nested object for each level of depth in consul.

Example json returned from consul:

[
  {
    "LockIndex": 0,
    "Key": "foobar/testing",
    "Flags": 0,
    "Value": "xxxxx",
    "CreateIndex": 833,
    "ModifyIndex": 833
  },
  {
    "LockIndex": 0,
    "Key": "foobar/bazbar",
    "Flags": 0,
    "Value": "xxxxx",
    "CreateIndex": 833,
    "ModifyIndex": 833
  }
]

Desired JSON:

[
  {
    "foobar": {
      "testing": "xxxxx",
      "bazbar": "xxxxx"
    }
  }
]

I'm sort of close with jq '.[] | objects | {Key: .Key | split("/"), Value: .Value}' but I'm just not understanding how I can recurse based on a split() of .Key and create nested objects. I think I'll also need to sort_by(.Key) to handle out of order data, unless I can | add arbitrarily and have jq resolve the structure.

I realize xxxxx is base64 encoded, and hopefully, base64d will get merged soon but until then I think I can handle decoding this with shell post-processing.

like image 280
brian Avatar asked Mar 22 '16 05:03

brian


2 Answers

The following produces the JSON object of interest in the particular case you give, but also provides a reasonable generalization in case there is more than one "foobar" prefix:

map( . as $o | .Key | split("/") | {(.[0]): {(.[1]): ($o|.Value) }} )
| reduce .[] as $o
    ( {};
      ($o|keys[0]) as $key | . + { ($key): (.[$key] + $o[$key]) } )

Output:

{
  "foobar": {
    "testing": "xxxxx",
    "bazbar": "xxxxx"
  }
}
like image 120
peak Avatar answered Oct 22 '22 15:10

peak


Here is a solution that uses reduce, split and setpath

[
  reduce (.[] | [(.Key | split("/")), .Value]) as [$p,$v] (
    {}
  ; setpath($p; $v)
  )
]
like image 21
jq170727 Avatar answered Oct 22 '22 15:10

jq170727