jsonnestedkeyjqinverse

How to get inverse operation of flatten for objects using jq?


Through another SO thread, I found how to "flatten" JSON using jq. I am looking how to do the inverse, i.e., "unflatten" it.

Original JSON has mixed levels in the hierarchy, e.g.:

{
  "X1": {
    "X1o1": {
      "X1o1o1": "abc",
      "X1o1o2": "def"
    },
    "X1o2" : {
      "X1o2o1" : "ghi",
      "X1o2o2": "jkl"
    },
    "X1o3": "mno"
  },
  "X2": "pqr"
}

As https://jqplay.org/s/Eo6h_3V8PO shows, for working with other colleagues, I need to "flatten" that structure () using

reduce (tostream|select(length==2)) as $i ({}; .[[$i[0][]|tostring]|join("_")] = $i[1] )

to produce:

{
  "X1_X1o1_X1o1o1": "abc",
  "X1_X1o1_X1o1o2": "def",
  "X1_X1o2_X1o2o1": "ghi",
  "X1_X1o2_X1o2o2": "jkl",
  "X1_X1o3": "mno",
  "X2": "pqr"
}

What I need help is how to transform such an output JSON back to the original form using jq?

I see that the split only works with strings as in tostring | split("_").

Any pointers and/or examples to guide me in the right direction would be much appreciated.


Solution

  • With your sample input, the filter:

    reduce to_entries[] as $kv ({}; setpath($kv.key|split("_"); $kv.value))
    

    produces:

    {
      "X1": {
        "X1o1": {
          "X1o1o1": "abc",
          "X1o1o2": "def"
        },
        "X1o2": {
          "X1o2o1": "ghi",
          "X1o2o2": "jkl"
        },
        "X1o3": "mno"
      },
      "X2": "pqr"
    }