Skip to content

API Reference

This comprehensive API reference is automatically generated from the source code docstrings using mkdocstrings. All classes, methods, and functions are documented with complete parameter descriptions, return values, and usage examples following NumPy documentation standards.


Package Overview

The jsonanatomy package provides a comprehensive suite of tools for JSON data structure exploration, safe navigation, and analysis. All primary components are available through the main package namespace for convenient access.

jsonanatomy

JSON Anatomy - A Python package for exploring and navigating JSON structures.

MIGRATION NOTICE

This package was previously named 'json-scout' with import name 'jsonscout'. If you're upgrading from json-scout: 1. Uninstall: pip uninstall json-scout 2. Install: pip install json-anatomy 3. Update imports: import jsonscout → import jsonanatomy

See https://github.com/deamonpog/json-anatomy/blob/main/MIGRATION.md

This package provides tools for introspecting, summarizing, and safely accessing nested JSON data structures. It includes utilities for file handling, safe data access patterns, and unified exploration interfaces.

Classes:

Name Description
Explore : class

Lightweight structural explorer for JSON objects.

Maybe : class

Monadic-style wrapper for safe optional traversal.

Xplore : class

Unified convenience facade combining all exploration tools.

SimpleXML : class

Utility for converting XML to nested dictionary structures.

Functions:

Name Description
get_json_file_paths : function

Find JSON files in a directory using glob patterns.

read_json_file : function

Read and parse JSON files with error handling.

Examples:

>>> import jsonanatomy as ja
>>> data = {'users': [{'name': 'Alice', 'age': 30}]}
>>> explorer = ja.Xplore(data)
>>> name = explorer['users'][0]['name'].value()
>>> print(name)  # 'Alice'

get_json_file_paths

get_json_file_paths(base_path, pattern='*.json')

Find JSON files in a directory using glob patterns.

Parameters:

Name Type Description Default
base_path str

The base directory path to search for JSON files.

required
pattern str

The glob pattern to match files, by default "*.json".

'*.json'

Returns:

Type Description
list of str

A list of absolute file paths to JSON files found in the directory.

Examples:

>>> json_files = get_json_file_paths('/path/to/data')
>>> print(json_files)
['/path/to/data/file1.json', '/path/to/data/file2.json']
>>> custom_files = get_json_file_paths('/path/to/data', 'config*.json')
>>> print(custom_files)
['/path/to/data/config_dev.json', '/path/to/data/config_prod.json']
Source code in src/jsonanatomy/file_reader.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_json_file_paths(base_path, pattern="*.json"):
    """
    Find JSON files in a directory using glob patterns.

    Parameters
    ----------
    base_path : str
        The base directory path to search for JSON files.
    pattern : str, optional
        The glob pattern to match files, by default "*.json".

    Returns
    -------
    list of str
        A list of absolute file paths to JSON files found in the directory.

    Examples
    --------
    >>> json_files = get_json_file_paths('/path/to/data')
    >>> print(json_files)
    ['/path/to/data/file1.json', '/path/to/data/file2.json']

    >>> custom_files = get_json_file_paths('/path/to/data', 'config*.json')
    >>> print(custom_files)
    ['/path/to/data/config_dev.json', '/path/to/data/config_prod.json']
    """
    json_files = glob.glob(os.path.join(base_path, pattern))
    return json_files

read_json_file

read_json_file(file_path, encoding='utf-8')

Read and parse a JSON file with error handling.

Parameters:

Name Type Description Default
file_path str

The absolute path to the JSON file to read.

required
encoding str

The file encoding to use when reading, by default "utf-8".

'utf-8'

Returns:

Type Description
dict or list

The parsed JSON data structure.

Raises:

Type Description
FileNotFoundError

If the specified file does not exist.

JSONDecodeError

If the file contents are not valid JSON.

Examples:

>>> data = read_json_file('/path/to/data.json')
>>> print(type(data))
<class 'dict'>
>>> data = read_json_file('/path/to/array.json')
>>> print(type(data))
<class 'list'>
Source code in src/jsonanatomy/file_reader.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def read_json_file(file_path, encoding="utf-8"):
    """
    Read and parse a JSON file with error handling.

    Parameters
    ----------
    file_path : str
        The absolute path to the JSON file to read.
    encoding : str, optional
        The file encoding to use when reading, by default "utf-8".

    Returns
    -------
    dict or list
        The parsed JSON data structure.

    Raises
    ------
    FileNotFoundError
        If the specified file does not exist.
    json.JSONDecodeError
        If the file contents are not valid JSON.

    Examples
    --------
    >>> data = read_json_file('/path/to/data.json')
    >>> print(type(data))
    <class 'dict'>

    >>> data = read_json_file('/path/to/array.json')
    >>> print(type(data))
    <class 'list'>
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"File not found at {file_path}")

    with open(file_path, "r", encoding=encoding) as file:
        data = json.load(file)
    return data

Core Modules

File Operations Module

The file_reader module provides essential utilities for locating and loading JSON data files from the filesystem with robust error handling and encoding support.

file_reader

File reader utilities for JSON file operations.

This module provides utility functions for finding and reading JSON files from the filesystem with proper error handling.

get_json_file_paths

get_json_file_paths(base_path, pattern='*.json')

Find JSON files in a directory using glob patterns.

Parameters:

Name Type Description Default
base_path str

The base directory path to search for JSON files.

required
pattern str

The glob pattern to match files, by default "*.json".

'*.json'

Returns:

Type Description
list of str

A list of absolute file paths to JSON files found in the directory.

Examples:

>>> json_files = get_json_file_paths('/path/to/data')
>>> print(json_files)
['/path/to/data/file1.json', '/path/to/data/file2.json']
>>> custom_files = get_json_file_paths('/path/to/data', 'config*.json')
>>> print(custom_files)
['/path/to/data/config_dev.json', '/path/to/data/config_prod.json']
Source code in src/jsonanatomy/file_reader.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_json_file_paths(base_path, pattern="*.json"):
    """
    Find JSON files in a directory using glob patterns.

    Parameters
    ----------
    base_path : str
        The base directory path to search for JSON files.
    pattern : str, optional
        The glob pattern to match files, by default "*.json".

    Returns
    -------
    list of str
        A list of absolute file paths to JSON files found in the directory.

    Examples
    --------
    >>> json_files = get_json_file_paths('/path/to/data')
    >>> print(json_files)
    ['/path/to/data/file1.json', '/path/to/data/file2.json']

    >>> custom_files = get_json_file_paths('/path/to/data', 'config*.json')
    >>> print(custom_files)
    ['/path/to/data/config_dev.json', '/path/to/data/config_prod.json']
    """
    json_files = glob.glob(os.path.join(base_path, pattern))
    return json_files

read_json_file

read_json_file(file_path, encoding='utf-8')

Read and parse a JSON file with error handling.

Parameters:

Name Type Description Default
file_path str

The absolute path to the JSON file to read.

required
encoding str

The file encoding to use when reading, by default "utf-8".

'utf-8'

Returns:

Type Description
dict or list

The parsed JSON data structure.

Raises:

Type Description
FileNotFoundError

If the specified file does not exist.

JSONDecodeError

If the file contents are not valid JSON.

Examples:

>>> data = read_json_file('/path/to/data.json')
>>> print(type(data))
<class 'dict'>
>>> data = read_json_file('/path/to/array.json')
>>> print(type(data))
<class 'list'>
Source code in src/jsonanatomy/file_reader.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def read_json_file(file_path, encoding="utf-8"):
    """
    Read and parse a JSON file with error handling.

    Parameters
    ----------
    file_path : str
        The absolute path to the JSON file to read.
    encoding : str, optional
        The file encoding to use when reading, by default "utf-8".

    Returns
    -------
    dict or list
        The parsed JSON data structure.

    Raises
    ------
    FileNotFoundError
        If the specified file does not exist.
    json.JSONDecodeError
        If the file contents are not valid JSON.

    Examples
    --------
    >>> data = read_json_file('/path/to/data.json')
    >>> print(type(data))
    <class 'dict'>

    >>> data = read_json_file('/path/to/array.json')
    >>> print(type(data))
    <class 'list'>
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"File not found at {file_path}")

    with open(file_path, "r", encoding=encoding) as file:
        data = json.load(file)
    return data

Structural Exploration Module

The Explore class offers lightweight structural analysis capabilities for JSON objects, enabling inspection of nested hierarchies and statistical analysis of data schemas.

Explore

Lightweight structural explorer for JSON objects.

This module provides the Explore class for basic inspection and navigation of nested JSON data structures (dictionaries and lists).

Explore

Explore(json_object)

A lightweight explorer for inspecting JSON object structures.

This class provides methods to examine the structure of JSON data, navigate through nested objects, and analyze the distribution of child properties across collections.

Parameters:

Name Type Description Default
json_object dict, list, or any

The JSON object to explore. Can be a dictionary, list, or any other type.

required

Attributes:

Name Type Description
data dict, list, or any

The original JSON object being explored.

child_keys list

A list of keys (for dicts) or indices (for lists) of direct children.

Examples:

>>> data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
>>> explorer = Explore(data)
>>> print(explorer.get_child_keys())
['users']
>>> users_explorer = explorer.explore_child('users')
>>> print(users_explorer.get_child_keys())
[0, 1]
Source code in src/jsonanatomy/Explore.py
39
40
41
42
43
44
45
def __init__(self, json_object):
    self.data = json_object
    self.child_keys = []
    if type(self.data) is dict:
        self.child_keys = list(self.data.keys())
    if type(self.data) is list:
        self.child_keys = [idx for idx in range(len(self.data))]

child

child(child_key)

Create a new Explore instance for a specific child.

Parameters:

Name Type Description Default
child_key str or int

The key (for dict) or index (for list) of the child to explore.

required

Returns:

Type Description
Explore

A new Explore instance wrapping the child object, or None if not found.

Examples:

>>> data = {'users': [{'name': 'Alice'}]}
>>> explorer = Explore(data)
>>> child = explorer.child('users')
>>> print(type(child.data))
<class 'list'>
Source code in src/jsonanatomy/Explore.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def child(self, child_key):
    """
    Create a new Explore instance for a specific child.

    Parameters
    ----------
    child_key : str or int
        The key (for dict) or index (for list) of the child to explore.

    Returns
    -------
    Explore
        A new Explore instance wrapping the child object, or None if not found.

    Examples
    --------
    >>> data = {'users': [{'name': 'Alice'}]}
    >>> explorer = Explore(data)
    >>> child = explorer.child('users')
    >>> print(type(child.data))
    <class 'list'>
    """
    if child_key in self.child_keys:
        return Explore(self.data[child_key])
    return Explore(None)

field_counts

field_counts(verbose=False)

Analyze the distribution of field names across all children in a collection.

This method examines each child of the current object and counts how frequently each field name appears across all children. Useful for understanding the schema of collections with varying structures.

Parameters:

Name Type Description Default
verbose bool

If True, print detailed exploration progress, by default False.

False

Returns:

Type Description
dict

A dictionary mapping field names to their occurrence counts.

Examples:

>>> data = {
...     'users': [
...         {'name': 'Alice', 'age': 30},
...         {'name': 'Bob', 'email': 'bob@example.com'},
...         {'name': 'Charlie', 'age': 25}
...     ]
... }
>>> explorer = Explore(data['users'])
>>> counts = explorer.field_counts()
>>> print(counts)
{'name': 3, 'age': 2, 'email': 1}
Notes

This method is particularly useful for analyzing collections where objects may have varying schemas or optional properties.

Source code in src/jsonanatomy/Explore.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def field_counts(self, verbose=False):
    """
    Analyze the distribution of field names across all children in a collection.

    This method examines each child of the current object and counts
    how frequently each field name appears across all children.
    Useful for understanding the schema of collections with varying structures.

    Parameters
    ----------
    verbose : bool, optional
        If True, print detailed exploration progress, by default False.

    Returns
    -------
    dict
        A dictionary mapping field names to their occurrence counts.

    Examples
    --------
    >>> data = {
    ...     'users': [
    ...         {'name': 'Alice', 'age': 30},
    ...         {'name': 'Bob', 'email': 'bob@example.com'},
    ...         {'name': 'Charlie', 'age': 25}
    ...     ]
    ... }
    >>> explorer = Explore(data['users'])
    >>> counts = explorer.field_counts()
    >>> print(counts)
    {'name': 3, 'age': 2, 'email': 1}

    Notes
    -----
    This method is particularly useful for analyzing collections where
    objects may have varying schemas or optional properties.
    """
    if verbose:
        print(f"Exploring grandchildren of type: {type(self.child_keys)} (size={len(self.data)}) with keys: {self.keys()}")
    counts = {}
    for child_key in self.child_keys:
        if verbose:
            print(f"Exploring child key: {child_key}")
        expChild = self.child(child_key)
        if verbose:
            print(f"  Child type: {type(expChild.data)} with keys: {expChild.keys()}")
        for grandChildKey in expChild.keys():
            if verbose:
                print(f"    Found grandchild key: {grandChildKey}")
            if grandChildKey in counts:
                counts[grandChildKey] += 1
            else:
                counts[grandChildKey] = 1

    return counts

field_counts_at

field_counts_at(depth=0)

Analyze field name distribution at a specified depth.

This method traverses the JSON structure to the given depth and then computes field counts for the objects found at that level.

Parameters:

Name Type Description Default
depth int

The depth level to analyze (0 = current level), by default 0.

0

Returns:

Type Description
dict

A dictionary mapping field names to their occurrence counts at the specified depth.

Source code in src/jsonanatomy/Explore.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def field_counts_at(self, depth = 0):
    """
    Analyze field name distribution at a specified depth.

    This method traverses the JSON structure to the given depth
    and then computes field counts for the objects found at that level.

    Parameters
    ----------
    depth : int, optional
        The depth level to analyze (0 = current level), by default 0.

    Returns
    -------
    dict
        A dictionary mapping field names to their occurrence counts
        at the specified depth."""
    if depth == 0:
        return self.field_counts()
    counts = {}
    for child_key in self.child_keys:
        expChild = self.child(child_key)
        child_counts = expChild.field_counts_at(depth - 1) if expChild else {}
        for field, count in child_counts.items():
            counts[field] = counts.get(field, 0) + count
    return counts

keys

keys()

Get the keys or indices of direct children.

Returns:

Type Description
list

For dictionaries: list of string keys. For lists: list of integer indices. For other types: empty list.

Examples:

>>> data = {'a': 1, 'b': 2}
>>> explorer = Explore(data)
>>> print(explorer.keys())
['a', 'b']
>>> data = [10, 20, 30]
>>> explorer = Explore(data)
>>> print(explorer.keys())
[0, 1, 2]
Source code in src/jsonanatomy/Explore.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def keys(self):
    """
    Get the keys or indices of direct children.

    Returns
    -------
    list
        For dictionaries: list of string keys.
        For lists: list of integer indices.
        For other types: empty list.

    Examples
    --------
    >>> data = {'a': 1, 'b': 2}
    >>> explorer = Explore(data)
    >>> print(explorer.keys())
    ['a', 'b']

    >>> data = [10, 20, 30]
    >>> explorer = Explore(data)
    >>> print(explorer.keys())
    [0, 1, 2]
    """
    return self.child_keys

value

value()

Get the underlying data object.

Returns:

Type Description
any

The wrapped data object being explored.

Examples:

>>> data = {'name': 'Alice', 'age': 30}
>>> explorer = Explore(data)
>>> original = explorer.value()  # Returns: {'name': 'Alice', 'age': 30}
>>> assert original is data  # Same object reference
Source code in src/jsonanatomy/Explore.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def value(self):
    """
    Get the underlying data object.

    Returns
    -------
    any
        The wrapped data object being explored.

    Examples
    --------
    >>> data = {'name': 'Alice', 'age': 30}
    >>> explorer = Explore(data)
    >>> original = explorer.value()  # Returns: {'name': 'Alice', 'age': 30}
    >>> assert original is data  # Same object reference
    """
    return self.data

Safe Access Module

The Maybe class implements a monadic pattern for safe, exception-free navigation through potentially incomplete or malformed JSON structures, supporting chainable operations.

Maybe

Safe access wrapper for JSON data structures.

This module provides the Maybe class, which implements a monadic-style pattern for safely accessing nested JSON data without raising exceptions when keys or indices don't exist.

Maybe

Maybe(json_object)

A wrapper for safe optional traversal over JSON data structures.

The Maybe class provides safe access to fields and indices without raising exceptions when accessing non-existent keys or out-of-bounds indices. This follows a monadic pattern where operations can be chained safely.

Parameters:

Name Type Description Default
json_object any

The JSON object to wrap. Can be a dict, list, or any other type.

required

Attributes:

Name Type Description
data any

The wrapped JSON object that may or may not exist.

Examples:

>>> data = {'name': 'Alice', 'age': 30}
>>> maybe = Maybe(data)
>>> name = maybe.field('name').value()  # 'Alice'
>>> missing = maybe.field('missing').value()  # None
>>> data = [10, 20, 30]
>>> maybe = Maybe(data)
>>> first = maybe.index(0).value()  # 10
>>> out_of_bounds = maybe.index(5).value()  # None
Source code in src/jsonanatomy/Maybe.py
40
41
def __init__(self, json_object):
    self.data = json_object

array

array(func=lambda k, o: o, filter=lambda k, o: True, as_type=list)

Safely convert a JSON array or object to a list of transformed items.

Applies a transformation function to each item in an array or each key-value pair in an object. Items can be filtered before transformation.

Parameters:

Name Type Description Default
func callable

Function to transform each item. For arrays: func(index, value). For objects: func(key, value). Default returns the value unchanged.

lambda k, o: o
filter callable

Function to filter items before transformation. Same signature as func. Default accepts all items.

lambda k, o: True
as_type type

The type constructor for the result container, by default list.

list

Returns:

Type Description
list or as_type

A container of transformed items, or empty container if not applicable.

Examples:

>>> maybe_list = Maybe([1, 2, 3])
>>> doubled = maybe_list.array(lambda k,v: v*2)  # [2, 4, 6]
>>> filtered = maybe_list.array(lambda k,v: v*2, lambda k,v: v > 2)  # [6]
>>> maybe_dict = Maybe({'a': 1, 'b': 2, 'c': 3})
>>> items = maybe_dict.array(lambda k,v: (k, v*2))  # [('a', 2), ('b', 4), ('c', 6)]
>>> filtered = maybe_dict.array(lambda k,v: (k, v*2), lambda k,v: v > 2)  # [('c', 6)]
>>> not_array = Maybe(42).array()  # []
Source code in src/jsonanatomy/Maybe.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def array(self, func=lambda k,o: o, filter=lambda k,o: True, as_type=list):
    """
    Safely convert a JSON array or object to a list of transformed items.

    Applies a transformation function to each item in an array or each 
    key-value pair in an object. Items can be filtered before transformation.

    Parameters
    ----------
    func : callable, optional
        Function to transform each item. For arrays: func(index, value).
        For objects: func(key, value). Default returns the value unchanged.
    filter : callable, optional
        Function to filter items before transformation. Same signature as func.
        Default accepts all items.
    as_type : type, optional
        The type constructor for the result container, by default list.

    Returns
    -------
    list or as_type
        A container of transformed items, or empty container if not applicable.

    Examples
    --------
    >>> maybe_list = Maybe([1, 2, 3])
    >>> doubled = maybe_list.array(lambda k,v: v*2)  # [2, 4, 6]
    >>> filtered = maybe_list.array(lambda k,v: v*2, lambda k,v: v > 2)  # [6]

    >>> maybe_dict = Maybe({'a': 1, 'b': 2, 'c': 3})
    >>> items = maybe_dict.array(lambda k,v: (k, v*2))  # [('a', 2), ('b', 4), ('c', 6)]
    >>> filtered = maybe_dict.array(lambda k,v: (k, v*2), lambda k,v: v > 2)  # [('c', 6)]

    >>> not_array = Maybe(42).array()  # []
    """
    if self.data is not None:
        if type(self.data) is dict:
            return as_type([func(key, obj) for key,obj in self.data.items() if filter(key, obj)])
        elif type(self.data) is list:
            return as_type([func(idx, obj) for idx,obj in enumerate(self.data) if filter(idx, obj)])
    return []

field

field(field)

Safely access a field in a JSON object (dict).

Parameters:

Name Type Description Default
field str

The field name to access in the dictionary.

required

Returns:

Type Description
Maybe

A new Maybe instance wrapping the field value or None if not present.

Examples:

>>> maybe = Maybe({'name': 'Alice'})
>>> name = maybe.field('name').value()  # 'Alice'
>>> age = maybe.field('age').value()    # None
Source code in src/jsonanatomy/Maybe.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def field(self, field):
    """
    Safely access a field in a JSON object (dict).

    Parameters
    ----------
    field : str
        The field name to access in the dictionary.

    Returns
    -------
    Maybe
        A new Maybe instance wrapping the field value or None if not present.

    Examples
    --------
    >>> maybe = Maybe({'name': 'Alice'})
    >>> name = maybe.field('name').value()  # 'Alice'
    >>> age = maybe.field('age').value()    # None
    """
    if self.data is not None and type(self.data) is dict and field in self.data:
        return Maybe(self.data[field])
    return Maybe(None)

filter

filter(func=lambda k, o: True)

Safely filter items in a JSON array or object.

Parameters:

Name Type Description Default
func callable

Function to determine which items to keep. For arrays: func(index, value). For objects: func(key, value). Default keeps all items.

lambda k, o: True

Returns:

Type Description
Maybe

A new Maybe wrapping filtered data of the same type, or None if not applicable.

Examples:

>>> maybe_dict = Maybe({'a': 1, 'b': 2, 'c': 3})
>>> filtered = maybe_dict.filter(lambda k,v: v > 2).value()  # {'c': 3}
>>> maybe_list = Maybe([1, 2, 3, 4])
>>> filtered = maybe_list.filter(lambda i,v: v % 2 == 0).value()  # [2, 4]
Source code in src/jsonanatomy/Maybe.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def filter(self, func=lambda k,o: True):
    """
    Safely filter items in a JSON array or object.

    Parameters
    ----------
    func : callable, optional
        Function to determine which items to keep. For arrays: func(index, value).
        For objects: func(key, value). Default keeps all items.

    Returns
    -------
    Maybe
        A new Maybe wrapping filtered data of the same type, or None if not applicable.

    Examples
    --------
    >>> maybe_dict = Maybe({'a': 1, 'b': 2, 'c': 3})
    >>> filtered = maybe_dict.filter(lambda k,v: v > 2).value()  # {'c': 3}

    >>> maybe_list = Maybe([1, 2, 3, 4])
    >>> filtered = maybe_list.filter(lambda i,v: v % 2 == 0).value()  # [2, 4]
    """
    if self.data is not None:
        if type(self.data) is dict:
            return Maybe({k: v for k,v in self.data.items() if func(k,v)})
        elif type(self.data) is list:
            return Maybe([obj for idx,obj in enumerate(self.data) if func(idx, obj)])
    return Maybe(None)

index

index(index)

Safely access an index in a JSON array (list).

Parameters:

Name Type Description Default
index int

The zero-based index to access in the list.

required

Returns:

Type Description
Maybe

A new Maybe instance wrapping the item value or None if not present.

Examples:

>>> maybe = Maybe([1, 2, 3])
>>> first = maybe.index(0).value()  # 1
>>> fourth = maybe.index(3).value()  # None
Source code in src/jsonanatomy/Maybe.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def index(self, index):
    """
    Safely access an index in a JSON array (list).

    Parameters
    ----------
    index : int
        The zero-based index to access in the list.

    Returns
    -------
    Maybe
        A new Maybe instance wrapping the item value or None if not present.

    Examples
    --------
    >>> maybe = Maybe([1, 2, 3])
    >>> first = maybe.index(0).value()  # 1
    >>> fourth = maybe.index(3).value()  # None
    """
    if self.data is not None and type(self.data) is list and index < len(self.data):
        return Maybe(self.data[index])
    return Maybe(None)

value

value()

Get the wrapped value.

Returns:

Type Description
any

The wrapped data object, which may be None if no value exists.

Examples:

>>> maybe = Maybe({'name': 'Alice'})
>>> data = maybe.value()  # {'name': 'Alice'}
>>> assert data == maybe.data  # Both access methods return same object
>>> empty = Maybe(None)
>>> data = empty.value()  # None
Source code in src/jsonanatomy/Maybe.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def value(self):
    """
    Get the wrapped value.

    Returns
    -------
    any
        The wrapped data object, which may be None if no value exists.

    Examples
    --------
    >>> maybe = Maybe({'name': 'Alice'})
    >>> data = maybe.value()  # {'name': 'Alice'}
    >>> assert data == maybe.data  # Both access methods return same object

    >>> empty = Maybe(None)
    >>> data = empty.value()  # None
    """
    return self.data

XML Processing Module

The SimpleXML class provides efficient XML-to-dictionary conversion capabilities for integrating XML data sources into JSON-based workflows. Built on lxml, it supports both well-formed XML and malformed/fragment XML with automatic fallback to lenient HTML parsing.

Key Features: - Powered by lxml for high performance and robustness - Automatic handling of XML fragments (missing root tags) - Lenient parsing fallback for malformed XML/HTML - Case-preserving when using strict XML mode - Tag frequency analysis for structural understanding

SimpleXML

Simple XML to dictionary converter.

This module provides a utility class for parsing XML strings and converting them to nested dictionary structures for easier JSON-like manipulation. Built on lxml for high performance and robust parsing with automatic handling of malformed XML and fragments.

SimpleXML

SimpleXML(xml_string)

A utility class for converting XML strings to nested dictionary structures.

This class provides robust XML parsing capabilities powered by lxml, with automatic handling of well-formed XML, XML fragments, and malformed markup. It converts XML elements to nested dictionaries and provides tag usage analysis.

Parsing Strategy: 1. Attempts strict XML parsing with lxml.etree 2. Falls back to lenient HTML parsing for malformed XML 3. Automatically wraps XML fragments (missing root tags) as needed 4. Preserves tag case when using strict XML mode

Parameters:

Name Type Description Default
xml_string str

The XML/HTML string to parse and convert. Can be well-formed XML, XML fragments without a root tag, or malformed HTML-like markup.

required

Attributes:

Name Type Description
data str

The original XML string.

root _Element

The parsed XML/HTML root element.

Notes

This class uses lxml for parsing, which provides: - High performance C-based parsing - Robust error recovery for malformed markup - Support for XML fragments and HTML5 - XPath and advanced XML features (accessible via root attribute)

Examples:

Well-formed XML:

>>> xml_data = '<users><user><name>Alice</name><age>30</age></user></users>'
>>> parser = SimpleXML(xml_data)
>>> result = parser.to_dict()
>>> print(result)
{'user': {'name': 'Alice', 'age': '30'}}

XML fragments (automatically wrapped):

>>> fragment = '<item>One</item><item>Two</item>'
>>> parser = SimpleXML(fragment)
>>> result = parser.to_dict()
>>> print(result)  # Wrapped in synthetic root
{'item': 'One'}

Malformed HTML-like markup (lenient parsing):

>>> malformed = '<div><p>Text<br><span>More</span></div>'
>>> parser = SimpleXML(malformed)
>>> result = parser.to_dict()  # Handles unclosed <br>
Source code in src/jsonanatomy/SimpleXML.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def __init__(self, xml_string):
    self.data = xml_string
    if not self.data:
        self.root = None
        return

    # Try parsing as strict XML first
    try:
        self.root = ET.fromstring(self.data)
    except ET.XMLSyntaxError:
        # Try HTML parser for malformed XML (with existing root)
        try:
            self.root = html.fromstring(self.data)
        except Exception:
            # If that fails, try wrapping in a root tag with XML parser
            try:
                wrapped = f'<root>{self.data}</root>'
                self.root = ET.fromstring(wrapped)
            except ET.XMLSyntaxError:
                # Last resort: wrap and use HTML parser
                wrapped = f'<root>{self.data}</root>'
                self.root = html.fromstring(wrapped)

analyze_tag_usagee

analyze_tag_usagee()

Analyze the frequency of XML tags in the document.

Returns:

Type Description
dict

A dictionary mapping tag names to their occurrence counts.

Examples:

>>> xml_data = '<root><item>1</item><item>2</item><name>test</name></root>'
>>> parser = SimpleXML(xml_data)
>>> counts = parser.analyze_tag_usagee()
>>> print(counts)
{'root': 1, 'item': 2, 'name': 1}
Notes

The method name contains a typo ('usagee' instead of 'usage') but is preserved for backward compatibility.

Source code in src/jsonanatomy/SimpleXML.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def analyze_tag_usagee(self):
    """
    Analyze the frequency of XML tags in the document.

    Returns
    -------
    dict
        A dictionary mapping tag names to their occurrence counts.

    Examples
    --------
    >>> xml_data = '<root><item>1</item><item>2</item><name>test</name></root>'
    >>> parser = SimpleXML(xml_data)
    >>> counts = parser.analyze_tag_usagee()
    >>> print(counts)
    {'root': 1, 'item': 2, 'name': 1}

    Notes
    -----
    The method name contains a typo ('usagee' instead of 'usage') but is
    preserved for backward compatibility.
    """
    tag_counts = {}
    self._count_tags(self.root, tag_counts)
    return tag_counts

to_dict

to_dict()

Convert the XML structure to a nested dictionary.

Returns:

Type Description
dict

A nested dictionary representation of the XML structure. Text content becomes string values, and nested elements become nested dictionaries.

Examples:

>>> xml_data = '<person><name>John</name><age>25</age></person>'
>>> parser = SimpleXML(xml_data)
>>> result = parser.to_dict()
>>> print(result)
{'name': 'John', 'age': '25'}
Source code in src/jsonanatomy/SimpleXML.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def to_dict(self):
    """
    Convert the XML structure to a nested dictionary.

    Returns
    -------
    dict
        A nested dictionary representation of the XML structure.
        Text content becomes string values, and nested elements become
        nested dictionaries.

    Examples
    --------
    >>> xml_data = '<person><name>John</name><age>25</age></person>'
    >>> parser = SimpleXML(xml_data)
    >>> result = parser.to_dict()
    >>> print(result)
    {'name': 'John', 'age': '25'}
    """
    return self._element_to_dict(self.root)

Unified Interface Module

The Xplore class serves as a comprehensive facade that combines the functionality of all core modules into a single, intuitive interface for streamlined JSON exploration workflows.

Xplore

Unified convenience facade for JSON exploration.

This module provides the Xplore class, which combines the functionality of Explore, Maybe, and SimpleXML into a single convenient interface for exploring and navigating JSON data structures.

Xplore

Xplore(data)

Unified convenience facade combining exploration tools.

This class provides a single entry point that wires together the functionality of Explore (structural exploration), Maybe (safe access), and SimpleXML (XML parsing) into one convenient interface.

Parameters:

Name Type Description Default
data any

The input data to be explored. Can be JSON data, XML string, or other data types.

required

Attributes:

Name Type Description
data any

The original input data.

explore Explore

An Explore instance for general data exploration.

maybe Maybe

A Maybe instance for safe data access operations.

xml SimpleXML or None

A SimpleXML instance if data is an XML string, None otherwise.

Examples:

>>> data = {'users': [{'name': 'Alice', 'age': 30}]}
>>> xplore = Xplore(data)
>>> name = xplore['users'][0]['name'].value()
>>> print(name)  # 'Alice'
>>> xml_data = '<user><name>Bob</name></user>'
>>> xplore = Xplore(xml_data)
>>> xml_dict = xplore.xml.to_dict()
>>> print(xml_dict)  # {'name': 'Bob'}
Notes
  • Creates an Explore instance for general data exploration
  • Creates a Maybe instance for safe data access operations
  • Creates a SimpleXML instance only if data is a string starting with "<"
  • The xml attribute will be None if data is not XML-formatted
Source code in src/jsonanatomy/Xplore.py
58
59
60
61
62
def __init__(self, data):
    self.data = data
    self.explore = Explore(data)
    self.maybe = Maybe(data)
    self.xml = SimpleXML(data) if isinstance(data, str) and data.strip().startswith("<") else None

children_with

children_with(predicate)

Get child keys that satisfy a given predicate.

Parameters:

Name Type Description Default
predicate function

A function that takes a child key and its value and returns True if the key should be included.

required

Returns:

Type Description
list

A list of child keys that satisfy the predicate.

Examples:

>>> data = {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
>>> xplore = Xplore(data)
>>> filtered = xplore.children_with(lambda k: k.startswith('a'))
>>> print(filtered)
['age', 'email']
Source code in src/jsonanatomy/Xplore.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
def children_with(self, predicate):
    """
    Get child keys that satisfy a given predicate.

    Parameters
    ----------
    predicate : function
        A function that takes a child key and its value and returns True
          if the key should be included.

    Returns
    -------
    list
        A list of child keys that satisfy the predicate.

    Examples
    --------
    >>> data = {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
    >>> xplore = Xplore(data)
    >>> filtered = xplore.children_with(lambda k: k.startswith('a'))
    >>> print(filtered)
    ['age', 'email']
    """
    return [key for key in self.keys() if predicate(key, self[key])]

contains

contains(value)

Check if the current data contains a specific value.

Returns:

Type Description
bool

True if the value is found, False otherwise.

Examples:

>>> data = [10, 20, 30]
>>> xplore = Xplore(data)
>>> print(xplore.contains(20))  # True
>>> print(xplore.contains(40))  # False
Source code in src/jsonanatomy/Xplore.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def contains(self, value):
    """
    Check if the current data contains a specific value.

    Returns
    -------
    bool
        True if the value is found, False otherwise.

    Examples
    --------
    >>> data = [10, 20, 30]
    >>> xplore = Xplore(data)
    >>> print(xplore.contains(20))  # True
    >>> print(xplore.contains(40))  # False
    """
    return value in self.data

field_counts

field_counts()

Get counts of fields.

Returns:

Type Description
dict

A dictionary mapping field names to their occurrence counts. Empty dict if data is not a list of dictionaries.

Examples:

>>> data = [{'name': 'Alice'}, {'name': 'Bob', 'age': 25}, {'age': 30}]
>>> xplore = Xplore(data)
>>> counts = xplore.field_counts()
>>> print(counts)  # {'name': 2, 'age': 2}
Source code in src/jsonanatomy/Xplore.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def field_counts(self):
    """
    Get counts of fields.

    Returns
    -------
    dict
        A dictionary mapping field names to their occurrence counts.
        Empty dict if data is not a list of dictionaries.

    Examples
    --------
    >>> data = [{'name': 'Alice'}, {'name': 'Bob', 'age': 25}, {'age': 30}]
    >>> xplore = Xplore(data)
    >>> counts = xplore.field_counts()
    >>> print(counts)  # {'name': 2, 'age': 2}
    """
    return self.explore.field_counts()

keys

keys()

Get the keys of the current data if it's a dictionary or list.

Returns:

Type Description
list

For dictionaries: list of string keys. For lists: list of integer indices. For other types: empty list.

Examples:

>>> data = {'name': 'Alice', 'age': 30}
>>> xplore = Xplore(data)
>>> print(xplore.keys())
['name', 'age']
>>> data = [10, 20, 30]
>>> xplore = Xplore(data)
>>> print(xplore.keys())
[0, 1, 2]
Source code in src/jsonanatomy/Xplore.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def keys(self):
    """
    Get the keys of the current data if it's a dictionary or list.

    Returns
    -------
    list
        For dictionaries: list of string keys.
        For lists: list of integer indices.
        For other types: empty list.

    Examples
    --------
    >>> data = {'name': 'Alice', 'age': 30}
    >>> xplore = Xplore(data)
    >>> print(xplore.keys())
    ['name', 'age']

    >>> data = [10, 20, 30]
    >>> xplore = Xplore(data)
    >>> print(xplore.keys())
    [0, 1, 2]
    """
    return self.explore.keys()

value

value()

Get the underlying data object.

Returns:

Type Description
any

The wrapped data object being explored.

Examples:

>>> data = {'name': 'Alice', 'age': 30}
>>> xplore = Xplore(data)
>>> original = xplore.value()  # Returns: {'name': 'Alice', 'age': 30}
>>> assert original is data  # Same object reference
Source code in src/jsonanatomy/Xplore.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def value(self):
    """
    Get the underlying data object.

    Returns
    -------
    any
        The wrapped data object being explored.

    Examples
    --------
    >>> data = {'name': 'Alice', 'age': 30}
    >>> xplore = Xplore(data)
    >>> original = xplore.value()  # Returns: {'name': 'Alice', 'age': 30}
    >>> assert original is data  # Same object reference
    """
    return self.data