0

I am currently working on a walking json where I could with parameters add in the walk I want to go through JSON. I have created something like this:

from collections import abc


def walk(obj, *path):
    """
    Goes through the given json path. If it is found then return the given path else empty dict
    """
    for segment in path:
        if not isinstance(obj, abc.Mapping) or segment not in obj:
            print(f"Couldn't walk path; {path}")
            return {}
        obj = obj[segment]
    return obj

# -------------------------------------------------------- #

json_value = {
    "id": "dc932304-dde4-3517-8b76-58081cc9dd0d",
    "Information": [{
        "merch": {
            "id": "8fb66657-b93d-5f2d-8fe7-a5e355f0f3a8",
            "status": "ACTIVE"
        },
        "value": {
            "country": "SE IS BEST"

        },
        "View": {
            "id": "9aae10f4-1b75-481d-ac5f-b17bc46675bd"
        }
    }],
    "collectionTermIds": [

    ],
    "resourceType": "thread",
    "rollup": {
        "totalThreads": 1,
        "threads": [

        ]
    },
    "collectionsv2": {
        "groupedCollectionTermIds": {

        },
        "collectionTermIds": [

        ]
    }
}

# -------------------------------------------------------- #

t = walk(json_value, "Information",  0)
print(t)

My current problem is that I am trying to get the the first in a list from "Information" by giving the walk function the value 0 as I provided however it returns that it couldn't due to Couldn't walk path; ('Information', 0)

I wonder how I can choose which list number I want to walk through by giving it into the parameter? e.g. if I would choose 1, it should return Couldn't walk path; ('Information', 1) but if I choose to do 0 then it should return

{
        "merch": {
            "id": "8fb66657-b93d-5f2d-8fe7-a5e355f0f3a8",
            "status": "ACTIVE"
        },
        "value": {
            "country": "SE IS BEST"

        },
        "View": {
            "id": "9aae10f4-1b75-481d-ac5f-b17bc46675bd"
        }
    }
PythonNewbie
  • 1,031
  • 1
  • 15
  • 33
  • Can't you just read in the json as a dictionary and then asked for the Information? I am pretty sure your json is going to be interpreted as a dict. – Kai Winkler Sep 08 '21 at 12:31
  • Your solution would then just be: def walk(obj, path, depth): // x = obj.get(path, "Could not walk path") // if not len(x) <= depth + 1: // print("Could not walk path") // else: // return x[depth] // – Kai Winkler Sep 08 '21 at 12:48
  • Hi @KaiWinkler - Well the problem would not solve if I example wouldn't want to go through the list. What if I example want to get the "View" instead, which I could do by doing `t = walk(json_value, "View")` but with your code that wouldn't work – PythonNewbie Sep 08 '21 at 13:18

1 Answers1

1

This should work for any JSON object:

def walk(obj, *path):
    """
    Goes through the given json path. If it is found then return the given path else empty dict
    """
    try:
        segment, key, *rest = path
    except ValueError:
        # We are simply passed in a single key, ex. walk(obj, 'my_key')
        key = path[0]
        try:
            return obj[key]
        except KeyError:
            print(f"Couldn't walk path: {key!r}\n"
                  f"  obj={obj!r}\n"
                  "  reason=key missing from object")
            return {}
        except TypeError:
            print(f"Couldn't walk path: {key!r}\n"
                  f"  obj={obj!r}\n"
                  "  reason=object is not a mapping or array type.")
            return {}
        except IndexError as e:
            print(f"Couldn't walk path: {key!r}\n"
                  f"  obj={obj!r}\n"
                  f"  reason={e}")
            return {}

    # This indicates we want to walk down a nested (or deeply nested) path. We
    # can use recursion to solve this case.

    try:
        inner_obj = obj[segment]
    except KeyError:
        print(f"Couldn't walk path: {segment!r} -> {key!r}\n"
              f"  obj={obj!r}\n"
              "  reason=key missing from object")
        return {}
    except IndexError as e:
        print(f"Couldn't walk path: {segment!r} -> {key!r}\n"
              f"  obj={obj!r}\n"
              f"  reason={e}")
        return {}
    else:
        return walk(inner_obj, key, *rest)

that could probably be optimized a bit, for example by removing the duplicate except blocks with a slight modification.

code for testing (using the json_value from above):

t = walk(json_value, 'Information', 1)
assert t == {}

t = walk(json_value, 'Information', 1, 'value', 'country')
assert t == {}

t = walk(json_value, 'Information', 0)
print(t)
# {'merch': {'id': '8fb66657-b93d-5f2d-8fe7-a5e355f0f3a8', 'status': 'ACTIVE'}, 'value': {'country': 'SE IS BEST'}, 'View': {'id': '9aae10f4-1b75-481d-ac5f-b17bc46675bd'}}

t = walk(json_value, 'Information', 0, 'value', 'country')
print(t)
# SE IS BEST

t = walk(json_value, 'collectionsv2', 'collectionTermIds')
print(t)
# []

t = walk(json_value, 'id')
print(t)
# dc932304-dde4-3517-8b76-58081cc9dd0d

t = walk(json_value, 'id', 'test')
assert t == {}
# error: wrong type
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53