1

I am trying to find all the full path for specific key. I tried with recursion, i can able to get the values, but i can't able to track the path using my code. Any Help will definitely useful for me.

Input:

{
  "id": ["0001"],
  "type": "donut",
  "name": "Cake",
  "ppu": 0.55,
  "batters": {
    "batter": {
      "id": ["1001"],
      "type": "Regular"
    }
  },
  "topping": [
    {
      "id": ["5001"],
      "type": "None"
    },
    {
      "id": ["5002"],
      "type": "Glazed"
    }
  ]
}

Expected Output:

[
    {"id":["0001"]},
    {"batters.batter.id":["1001"]},
    {"topping[0].id":["5001"]},
    {"topping[0].id":["5002"]}
]

Following code is i have used for get the values, but it doesn't helped me.

def json_extract(obj, key):
    """Recursively fetch values from nested JSON."""
    arr = []

    def extract(obj, arr, key):

        """Recursively search for values of key in JSON tree."""
        if isinstance(obj, dict):
            for k, v in obj.items():
                if isinstance(v, (dict)):
                    extract(v, arr, key)
                elif k == key:
                    arr.append(v)
        elif isinstance(obj, list):
            for item in obj:
                extract(item, arr, key)
        return arr

    values = extract(obj, arr, key)
    return values
NAGA RAJ S
  • 452
  • 4
  • 12
  • How do you envision the signature of such function? – Marat Jul 29 '22 at 16:45
  • I planned traverse through the end of the target, need to track the nodes while traversing through. if i reached the endpoint i need to grab the tracked path and store it in array. I know in above code does not track any value. – NAGA RAJ S Jul 29 '22 at 16:56
  • You can track value as you go inside the loop and then you will easily able to merge them to get the full path. – Shantanu Tripathi Jul 29 '22 at 17:01
  • 3
    @NAGARAJS, like [this](https://tio.run/##dVLLasMwELzrK4RONpjgtARKwaceeuuh1xCCGq1TYVsWkhxwjL/dXcmPNMHZg9GOZmZXg3Xrfmv1@qbNMAjI6RncUQob2boxJ0hoAW3GWPxOKJbMqbRSWcfVCRaKkCc3EXzltaFFQi9UKjpSNtJBZaN/HF@u0kd0pxnNWYeHftMVPfMzPAqlBVrcCfwNzTLKpGD3Vr5aCaWYTXH8HcG7PZPkpq6WZ1@S2SImo3D9zVEprUNuo0vsLOAZfa6g8Bg/hCGnMEA1FRjuZpeHPNa3mbLZd7I/sJgQwR3HyDqUhhzonqVpumWHxCOu1YAYE7VqHAuQ4lWAPngBI6J1g0C62e1C@8OdA2MR6sI@E7D0t0HbZVBA52HfcG5KbljAe/z24y611lKdvTLcPLrt1t2@agWTVfJM97Ki@yz5FcRtiQPpCTFgm9JhXv4fjeZYfYZxPAx/)? – Olvin Roght Jul 29 '22 at 17:01
  • Yes i tried, i am losing while tracking. – NAGA RAJ S Jul 29 '22 at 17:02
  • Function signature stands for: what kind of inputs does it accept and what is the output format. Try to write a call to an imaginary function that will transform the example output to the desired output – Marat Jul 29 '22 at 17:06

2 Answers2

1

Thanks for providing solution Olvin Right.

def get_paths(source, key="", target = None):
    if isinstance(source, dict):
        for source_key, source_value in source.items():
            tmp_key = f"{key}.{source_key}" if key else source_key
            if source_key == target:
                yield tmp_key, source_value, source.get('_type')
            else:
                yield from get_paths(source_value, tmp_key, target)
    elif isinstance(source, (list, tuple, set, frozenset)):
        for index, value in enumerate(source):
            yield from get_paths(value, f"{key}[{index}]", target)
NAGA RAJ S
  • 452
  • 4
  • 12
  • 2
    I'd recommend you to swap positions of `target` and `key` arguments as `key` generally won't be used in regular call, just in recursive, so it's common to define it last and set default value. – Olvin Roght Jul 30 '22 at 15:13
  • Remark: it wont work as in @OlvinRoght 's linked comment (see question), due to the third always `None` yielding `source.get('_type')`. Is this desirable/expected? – BadAtLaTeX Aug 04 '23 at 17:06
0

You can use this code to acheive the same (not optimized though).

import json
# Opening JSON file
f = open('del.json')
data = json.load(f)
arr = []
def json_extract(obj, key):
    def dfs(obj, key, prefix):
        for k, v in obj.items():
            if(isinstance(v, dict)):
                if(k == key):
                    arr.append((prefix+"."+k if prefix else k, v))
                else:
                    dfs(v, key, prefix + "." + k if prefix else k)
            elif(isinstance(v, list)):
                if(k == key):
                    arr.append((prefix+"."+k if prefix else k, v))
                else:
                    for i, val in enumerate(v):
                        dfs(val, key, prefix + k + f"[{i}]")
  
    dfs(obj, key, "")
    
json_extract(data, "id")
for i in arr:
    print(i)