简体   繁体   中英

How do I reconstruct JSON from a dot delimeted list of strings?

One would think doing this is easy. I swear it is, just... I can't seem to figure it out. How do I transform:

terrible_way_to_describe_nested_json=['abc','abd','a.e','a.f','g.h']

into

{
    "a": {
        "b": {
            "c": None,
            "d": None
        },
        "e": None,
        "f": None
    },
    "g": {
        "h": None
    }
}

If you would consider 'abc' a path of a deconstructed JSON load, then I have about 200 of these unsorted paths (transformation should work regardless of order) that go up to 8 dots deep all eagerly hoping to become part of their original structure. I've tried approaching this using recursion, pandas to sort leaf nodes from internal ones (ridiculous?), crazy list of lists of dictionaries of lists who knows, even autovivification .

Field of ruin and despair

Here's one of 6 partial implementations I've written/abandoned. It goes as far as peeling back the layer of nested keys right before the fringe nodes then I loose my mind. I'd almost recommend ignoring it.

def dot_to_json(dotted_paths):
    scope_map=[line.split('.') for line in dotted_paths] #Convert dots list to strings
    # Sort and group list of strings according to length of list. longest group is last
    b=[]
    for index in range(max([len(x) for x in scope_map])+1):
        a=[]
        for item in scope_map:
            if len(item)==index:
                a.append(item)
        b.append(a)
    sorted_nest=[x for x in b if x] # finally group according to list length
    #Point AA
    # group string list 'prefix' with key:value
    child_path=[]
    for item in sorted_nest[-1]:
        child_path.append([item[:-1],{item[-1]:None}])
    # peel back a layer
    new_child_path=[]
    for scope in scope_map[-2]:
        value=None # set value to None if fringe node
        for index, path in enumerate(child_path):
            if path[0]==scope: # else, save key + value as a value to the new prefix key
                value=path[1]
                child_path.pop(index) # 'move' this path off child_path list
        new_child_path.append([scope[:-1],{scope[-1]:value}])
    new_child_path+=child_path
    #Point BB...
    #Loop in some intelligent way between Point AA and Point BB
    return new_child_path
#%%
dotted_json=['a.b.c','a.b.d','a.e','a.f','g.h']

scope_map=dot_to_json(dotted_json)

Here you go:

In [5]: terrible_way_to_describe_nested_json=['a.b.c','a.b.d','a.e','a.f','g.h']

In [6]: terrible_way_to_describe_nested_json = [s.split('.') for s in terrible_way_to_describe_nested_json]

In [7]: data = {}

In [8]: for path in terrible_way_to_describe_nested_json:
   ....:     curr = data
   ....:     for i, node in enumerate(path):
   ....:         if i == len(path) - 1:
   ....:             curr[node] = None
   ....:         else:
   ....:             curr = curr.setdefault(node,{})
   ....:             

In [9]: data
Out[9]: {'a': {'b': {'c': None, 'd': None}, 'e': None, 'f': None}, 'g': {'h': None}}

Now pretty printing using the json module gives:

{
    "a": {
        "f": null,
        "b": {
            "d": null,
            "c": null
        },
        "e": null
    },
    "g": {
        "h": null
    }
}

Those two should be equivalent but out of order, or at least, printed out of order.

Try out the dpath module, it can be used to add/filter/search dictionaries in an easy way. By replacing the dots with '/' characters you can create the dict you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM