Extract nodes from json based on user input preserveing a portion of the higher level object as well

Question:

need to extract object from the given json based on the node chain passed by user and neglect those which are not in
user input, then create a new json object

my master json is :

{
"menustructure": 
[
        {
         "node":"Admin",
         "path":"admin",
            "child":[
                    {
                        "id": "resouce0",
                        "node": "Admin.resouce0",
                        "path":"resouce0",
                        "rank":0,
                        "child":[
                             {   
                                "id": "res_child",
                                "node": "Admin.resouce0.res_child",
                                "path":"res_child",
                                "rank":1
                             },
                             {   
                                "id": "res_child2",
                                "node": "Admin.resouce0.res_child2",
                                "path":"res_child",
                                "rank":1
                             }
                                ]
                     },
                    {
                        "id": "resouce1",
                        "node": "Admin.resouce1",
                        "path":"resouce1",
                        "rank":1
                     },
                
                    {
                        "id": "resouce2",
                        "node":"Admin.resouce2",
                        "path": "oath",
                        "rank":2
                    }
                   ]
        },
        {
            "node":"Workspace",
            "path": "wsp",
            "child":[{
                    "id":"system1",
                    "node": "Workspace.system1",
                    "path":"sys1",
                    "child":[{
                           "id": "child1",
                           "node": "Workspace.system1.child1",
                           "path":"ch1"
                
                        }]
                
                },
                {   
                    "id":"system2",
                    "node": "Workspace.system2",
                    "path":"sys2"
                }
            ]
        }]}

for example if user pass ['Admin.resource1', 'Workspace'] so expeceted ouput json will be
Note ‘.’ in element of user inputted list means that node have child nodes and new json will be having all those child node details including parent node details.

{
    "menustructure": 
    [
            {
             "node":"Admin",
             "path":"admin",
                "child":[
                        {   "id": "resouce1",
                            "node": "Admin.resouce1",
                            "path":"resouce1",
                            "rank":1
                         }
                       ]
            },
            {
                "node":"Workspace",
                "path": "wsp",
                "child":[{
                        "id": "system1",
                        "node": "Workspace.system1",
                        "path":"sys1"
                         "child":[{
                           "id": "child1",
                           "node": "Workspace.system1.child1",
                           "path":"ch1"
                    
                    },
                    {   "id": "system2",
                        "node": "Workspace.system2",
                        "path":"sys2"
                    }
                ]
            }

        
    ]
}

or another example is : ['Admin.resouce2', 'workspace.system1'] then expected json will be:

{
    "menustructure": 
    [
            {
             "node":"Admin",
             "path":"admin",
                "child":[
                        
                        {"id": "resouce2","node":"Admin.resouce2",
                            "path": "oath",
                            "rank":2
                        }
                       ]
            },
            {
                "node":"Workspace",
                "path": "wsp",
                "child":[{
                        "id": "system1",
                        "node": "Workspace.system1",
                        "path":"sys1"
                        "child":[{
                           "id": "child1",
                           "node": "Workspace.system1.child1",
                           "path":"ch1"
                    
                    }
                ]
            }
    ]
}

or if only single node passed ['Admin'] then output json will be:

{
    "menustructure": 
    [
            {
             "node":"Admin",
             "path":"admin",
                "child":[
                        {
                            "id": "resouce1",
                            "node": "Admin.resouce1",
                            "path":"resouce1",
                            "rank":1
                         },
                    
                        {"id": "resouce2","node":"Admin.resouce2",
                            "path": "oath",
                            "rank":2
                        }
                       ]
            }   
    ]
}

Code I tried is working for one level of child:

master = json.loads(m)
menustruct = []
test_master = master['menustructure']
temp_json = test_master
nde = ['Admin.resouce1', 'Admin.resouce0', 'Workspace.system2']
temp_data = master['menustructure']
#print(temp_data)
final_data = []
parent_node = []
for m in nde:
    items = copy.deepcopy(temp_data)
    if "." in m:
        menu_series = m.split(".")
        for item in items:
            if item['node'] == menu_series[0]:
                item_child_nodes = item['child']
                child = None
                for node in item_child_nodes:
                    if node['id'] != menu_series[1]:
                        item_child_nodes.remove(node)
                    else:
                        child = node

                if menu_series[0] in parent_node:
                    for i in final_data:
                        if i['node'] == menu_series[0]:
                            i['child'].append(child)
                else:
                    final_data.append(item)
                #print(item_child_nodes)
        parent_node.append(menu_series[0])

    else:
        for item in items:
            if item['node'] == m:
                final_data.append(item)
t = {}
t['menustructure'] = final_data
print(t)

but not getting how to handle multiple child level for example

{master -> child -> child} or {master -> child -> child -> child}

multilevel child is present in Workspace.system1
If child parent already exist then child should get appended into parent node in resulting json

I tried Glom lib but it’s not working as intended.
Any help on how to achieve multi level child problem.

Asked By: vineet singh

||

Answers:

Here is my solution. The idea is to traverse the structure recursively and remove nodes that don’t match user input. The algorithm does not mutate the input data, but creates a shallow copy of the subtree only when the child attribute is changed.

def extract(data, query):
    return {
        "menustructure": extract_nodes(
            data["menustructure"], [x.split(".") for x in query]
        )
    }


def matches(name, query):
    name = name.split(".")
    for q in query:
        size = min(len(q), len(name))
        if name[:size] == q[:size]:
            return True

    return False


def extract_nodes(data, query):
    if isinstance(data, list):
        data = [
            extract_nodes(x, query)
            for x in data
            if matches(x["node"], query)
        ]
        return [x for x in data if x is not None]

    if isinstance(data, dict) and matches(data["node"], query):
        if "child" in data:
            children = extract_nodes(data["child"], query)
            if len(children) != len(data["child"]):
                data = data.copy()  # copy-on-write
                data["child"] = children
            if not data["child"]:
                return None
        return data

Usage:

import json
data = json.loads(m)
result = extract(data, ["Admin.resouce1", "Workspace.system1"])
print(json.dumps(result, indent=4))


result = extract(data, ["Admin.resouce0.res_child"])
print(json.dumps(result, indent=4))
Answered By: Alexander Volkovsky
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.