JSON Walkers

Description

JSONWalker and JSONSplitWalker classes let you navigate JSON easier without a need to constantly handle exceptions. You can simply provide full JSON path to the value you want and than check if it exists.

They use the syntax similar to pathlib Path objects, they overload the division (/) and integer division (//) operators to create paths.

/ is used to create simple paths, where the keys are either strings (for accessing objects) or integers (for accesing arrays) or JSONPath objects which internally are just tuples of strings and integers representing a longer path.

// is used to create split paths, when applied to a JSON walker, it creates a JSON split walker, which has a list of JSON walkers representing all of the matching paths. The arguments of this operations can be following:

  • literal str - matches any key of the object

  • literal int - matches any index of the array

  • None - matches any key or index

  • literal SKIP_LIST - can be used to enter every item of an array, or if current value is an object, it returns that object. It’s useful when the structure of the JSON file allows the item to be either an object or a list of objects.

  • string values are interpreted as regular expressions, which are matched against the keys of the objects.

Code example

from better_json_tools import JSONWalker

with open('file.json', 'r') as f:
    walker = JSONWalker.load(f)

# the content of the file
print(walker.data)
# output:
# {'a': 1, 'b': [{'x': 1, 'y': 2}, {'x': 4, 'y': 5}],
# 'c': {'c1': {'x': 11, 'y': 22}, 'c2': {'x': 44, 'y': 55}}}

# the value of 'a' property
print((walker / 'a').data)
# output:
# 1

# the value of any list element from 'b' property
for i in (walker / 'b' // int).data:
    print(i.data)
# output:
# {'x': 1, 'y': 2}
# {'x': 4, 'y': 5}

# the 'x' value of any item (from list or object) from any object that
# matches the '[a-z]' regex
for i in (walker // '[a-z]' // None / 'x').data:
    print(i.data)
# output:
# 1
# 4
# 11
# 44

# Creating JSON paths using create_path method
data = JSONWalker({})

new_path = data /'a' / 'b'
new_path.create_path("Hello")
# data:
# {"a": {"b": "Hello"}}

new_path = data / 'c' / 3
new_path.create_path("Test", empty_list_item_factory=lambda: "abc")
# data:
# {"a": {"b": "Hello"}, "c": ["abc", "abc", "abc", "Test"]}

Classes

class JSONPath(path: Union[str, tuple[Union[str, int], ...]])[source]

Represents a path in a JSON file. The paths internally use a tuple listing the keys, but they can be represented or created from a string. The string representation is similar to the one used in JavaScript.

The path objects can be used to access the data of JSONWalker.

Example

>>> from better_json_tools import JSONPath
>>> path = JSONPath(("a", "$abc", 1, 2, 'with quote "'))
>>> print(path.data)
... ('a', '$abc', 1, 2, 'with quote "')
>>> print(path)
... a.$abc[1][2]["with quote ""]
>>> another_path = JSONPath(str(path))
>>> print(another_path)
... a.$abc[1][2]["with quote ""]
>>> print(another_path.data == path.data)
... True
class JSONWalker(data: Union[dict[str, Any], list[Any], str, float, int, bool, None, Exception], *, parent: Optional[JSONWalker] = None, parent_key: Optional[Union[str, int]] = None)[source]

A class that represents a path in the JSON file for easy access to its values.

__add__(other: Union[JSONSplitWalker, JSONWalker]) JSONSplitWalker[source]

The + operator adds json walkers creating a split walker with more values.

__floordiv__(key: Union[str, Type[int], Type[str], None, Type[SKIP_LIST]]) JSONSplitWalker[source]

The // operator creates JSONSplitWalker object with multiple alternative paths that matched provided key.

Raises

TypeError - invalid input data type

re.error - invlid regular expression.

__truediv__(key: Union[str, int, JSONPath]) JSONWalker[source]

The / operator creates descendant path in the JSON file.

create_path(data: Optional[Union[dict[str, Any], list[Any], str, float, int, bool]], *, exists_ok: bool = True, can_break_data_structure: bool = True, can_create_empty_list_items: bool = True, empty_list_item_factory: Optional[Callable[[], Optional[Union[dict[str, Any], list[Any], str, float, int, bool]]]] = None)[source]

Creates path to the part of JSON file pointed by this walker.

Parameters
  • data – the data to put at the end of the path.

  • exists_ok – if False, the ValueError will be risen if the path to this item already exists.

  • can_break_data_structure – if True than the function will be able to replace certain existing paths with dicts or lists. Example - if path “a”/”b”/”c” points at integer, creating path “a”/”b”/”c”/”d” will replace this integer with a dict in order to make “d” a valid key. Setting this to false would cause a KeyError in this situation.

  • can_create_empty_list_items – enables filling up the lists in JSON with values produced by the empty_list_item_factory in order to match the item index specified in the path. Example - if you specify a path to create “a”/5/”c” but the list at “a” path only has 2 items, then the function will create additional item so the 5th index can be valid.

  • empty_list_item_factory – a function used to create items for lists in order to make indices specified in the path valid (see can_create_empty_list_items function parameter). If this value is left as None than the lists will be filled with null values.

property data: Union[dict[str, Any], list[Any], str, float, int, bool, None, Exception]

The data from JSON that this walker points to.

property exists: bool

Returns true if path to this item already exists. This function recursively checks the entire path to this item starting from root so even if the object is detached from the root somewhere in the middle of the path, the function will still return correct value.

static load(json_file: IO[Any], **kwargs: Any) JSONWalker[source]

Creates json walker using json.load() function. Passes all arguments to json.load and tries to creat the walker base on the result.

static loads(json_text: Union[str, bytes], **kwargs: Any) JSONWalker[source]

Creates json walker using json.loads() function. Passes all arguments to json.loads and tries to creat the walker base on the result.

property parent: JSONWalker

The parent of this json walker (the walker that created this walker).

Rises

KeyError when this JSONWalker is a root object.

property parent_key: Union[str, int]

The key used to access this walker from its parent

Rises

KeyError when this JSONWalker is a root object

property path: tuple[Union[str, int], ...]

Full JSON path up to this point starting from the root of the JSON file in from of a tuple of keys.

property path_str: str

Full JSON path up to this point starting from the root of the JSON file in form of a string.

property root: JSONWalker

The root object of this JSON file.

class JSONSplitWalker(data: list[better_json_tools.json_walker.JSONWalker])[source]

Multiple walker objects grouped together. This class can be browse JSON file contents from multiple JSON paths at once.

__add__(other: Union[JSONSplitWalker, JSONWalker]) JSONSplitWalker[source]

The + operator adds json walkers creating a split walker with more values.

__floordiv__(key: Union[str, Type[int], Type[str], None, Type[SKIP_LIST]]) JSONSplitWalker[source]

Applies // operator to all of the JSONWalkers in this split walker, creating even more split walkers (all groupped together in one object).

__iter__() Iterator[JSONWalker][source]

Yield every walker contained in this object.

__truediv__(key: Union[str, int, JSONPath]) JSONSplitWalker[source]

Applies / operator to all of the JSONWalkers in this split walker.

property data: list[better_json_tools.json_walker.JSONWalker]

The list of the JSONWalker objects contained in this object.