Home > Enterprise >  Logic to convert flat JSON to nested JSON
Logic to convert flat JSON to nested JSON

Time:11-10

I have a flat JSON where keys represent different levels.

For example:

data = {
      "name": "John",
      "age": 30,
      "address:city": "New-York",
      "address:street": "5th avenue",
      "address:number": 10,
}

As you can see keys contains : which is the separator for the level.

I would like to convert it to have something like this:

wanted = {
      "name": "John",
      "age": 30,
      "address": {
            "city": "New-York",
            "street": "5th avenue",
            "number": 10
      }
}

I'm working with Python but here I'm more looking just for logic advice, what would be the best approach to solve this problem in a generic way (not for this specific example only)

Thanks for your help

CodePudding user response:

I think an easy approach would be to create a method that maps this JSON to an object, and then convert the object to another object with attributes such as in the "wanted" one, and then you can simply convert the result object to JSON that will have "wanted" structure

CodePudding user response:

In Python:

def convert(inp, sep=':'):
    output = {}
    for key, value in inp.items():
        *path, skey = key.split(sep)
        dest = output
        for p in path:
            if p not in dest:
                dest[p] = dict()
            dest = dest[p]
        dest[skey] = value
    return output

CodePudding user response:

A recursive approach that supports multiple levels of nesting.

from typing import Any

def build(key: list[str], value: Any, res: dict):
    k, *r = key
    if r:
        if not res.get(k):
            res[k] = {}
        build(r, value, res[k])
    else:
        res[k] = value

res = {}
for k, v in data.items():
    build(k.split(":"), v, res)

CodePudding user response:

You can use jq:

jq 'reduce to_entries[] as {$key, $value} ({}; setpath($key / ":"; $value))'

Output

{
  "name": "John",
  "age": 30,
  "address": {
    "city": "New-York",
    "street": "5th avenue",
    "number": 10
  }
}

If you want the keys sorted:

jq '. as $obj
    | reduce keys[] as $key ({}; . * setpath($key | split(":"); $obj[$key]))'

Output

{
  "address": {
    "city": "New-York",
    "number": 10,
    "street": "5th avenue"
  },
  "age": 30,
  "name": "John"
}

The code works for more than one nested level (e.g. additional entry "contact:phone:work": "110")

CodePudding user response:


# Data input
data = {
      "name": "John",
      "age": 30,
      "address:city": "New-York",
      "address:street": "5th avenue",
      "address:number": 10,
}

# Initiate output
output = {

}

# Get keys in data
keys = data.keys()

# Iterate through keys
for i in keys:
    
    # If there is a seperator, make sub-dcitionary. If not then proceed to add data normally.
    if ":" in i:

        # Extract subdictionary title and element name
        split = i.split(":")

        # If the subdictionary doesn't already exist, create it
        if not (split[0] in output.keys()):
            output[split[0]] = {}

        # Add element to subdictionary
        output[split[0]][split[1]]=data[i]
    else:
        output[i]=data[i]

# Print out results
print(output)

Results

{'name': 'John', 'age': 30, 'address': {'city': 'New-York', 'street': '5th avenue', 'number': 10}}
  • Related