Sam P
Sam P

Reputation: 103

Convert Tree of key value pairs to a json object

I'm working with an API that is returning data in a strange "tree-like" data structure format that I'm trying to convert it to a proper JSON object for re-usability.

It is somewhat like a "tree" structure and I've had luck converting just the "keys" but I'm having trouble associating the values with it. The following answer https://stackoverflow.com/a/9619101/1899406 seems to be a good starting point.

Code:

dct = {}
for key in config.keys():
    p = dct
    for x in key.split('/'):
        p = p.setdefault(x, {})

Starting data:

{   u'key': 
    u'/parent/child/child2/keyname1',
    u'value': u'arbitrary value'},
{   u'key': u'/parent/child/keyname2',
    u'value': u'arbitrary value2'},
{   u'key': 
    u'/parent/child/child2/child3/child4/child5/keyname3',
    u'value': u'arbitrary value3'},
{   u'key': u'/parent/keyname4',
    u'value': u'arbitrary value4'},

...

Cleaned up slightly:

{{   u'/parent/child/child2/keyname1':
    u'arbitrary value'},
{   u'/parent/child/keyname2':
    u'arbitrary value2'},
{   u'/parent/child/child2/child3/child4/child5/keyname3':
    u'arbitrary value3'},
{   u'/parent/keyname4':
    u'arbitrary value4'}}

Desired format(roughly):

{
    u'parent': {
        u'keyname4': u'arbitrary value4',
        u'child': {
            u'keyname2': u'arbitrary value2',
            u'child2': {
                u'keyname1': u'arbitrary value',
                u'child3': {
                    u'child4': {
                        u'child5': {
                            u'keyname4': u'arbitrary value4'
                                   }
                               }
                           }
                       }
                  }
               }
}

Upvotes: 3

Views: 478

Answers (1)

Mark
Mark

Reputation: 92440

It seems like you need to split the key values on / then build an object with each. dict.setdefault() is super handy here because it lets you grab the a value at a key or set it to a default and grab that if the key is not yet defined.

This uses setdefault() in reduce (you could also use a for loop and just keep track of the current dictionary) to iterate down to the second to last key. Then set the value with the last key:

from functools import reduce

d = {}
for item in tree:
    for k, v in item.items():
        path = k.split('/')
        parent = reduce(lambda curr, x: curr.setdefault(x, {}), path[1:-1], d)
        parent[path[-1]] = v

at the end d should look like:

{'parent': {
    'child': {
         'child2': {'keyname1': 'arbitrary value',
                    'child3': {
                         'child4': {
                             'child5': {'keyname3': 'arbitrary value3'}
                          }
                     }
           },
          'keyname2': 'arbitrary value2'},
          'keyname4': 'arbitrary value4'}
  } # i *think* I got the indents right!

Upvotes: 1

Related Questions