jsonobjectify 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jsonobjectify-0.0.0/PKG-INFO +115 -0
- jsonobjectify-0.0.0/README.md +104 -0
- jsonobjectify-0.0.0/pyproject.toml +105 -0
- jsonobjectify-0.0.0/pyproject.toml.orig +107 -0
- jsonobjectify-0.0.0/src/jsonobjectify/__init__.py +0 -0
- jsonobjectify-0.0.0/src/jsonobjectify/json_object.py +40 -0
- jsonobjectify-0.0.0/src/jsonobjectify/json_path.py +100 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: jsonobjectify
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: A lightweight Python library for converting JSON data into Python objects, simplifying access to nested structures through intuitive dot notation.
|
|
5
|
+
Author: Sandeep Suryaprasad
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Project-URL: Homepage, https://github.com/sandeepsuryaprasad/jsonobjectify
|
|
8
|
+
Project-URL: Repository, https://github.com/sandeepsuryaprasad/jsonobjectify
|
|
9
|
+
Project-URL: Issues, https://github.com/sandeepsuryaprasad/jsonobjectify/issues
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# jsonobjectify
|
|
13
|
+
|
|
14
|
+
A lightweight Python library for converting JSON data into Python objects,
|
|
15
|
+
simplifying access to nested structures through intuitive dot notation.
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
Working with complex JSON data often involves repetitive dictionary lookups, especially
|
|
19
|
+
when accessing deeply nested structures. `jsonobjectify` simplifies this process by
|
|
20
|
+
converting JSON data into Python objects, allowing you to access JSON properties using
|
|
21
|
+
familiar attribute notation.
|
|
22
|
+
|
|
23
|
+
Instead of writing chained dictionary lookups, you can navigate nested JSON structures
|
|
24
|
+
using a clean, readable, object-oriented interface.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
* **Dot-notation access:** Access JSON properties using Python attributes.
|
|
29
|
+
* **Nested object support:** Navigate deeply nested JSON structures naturally.
|
|
30
|
+
* **List support:** Work with collections of JSON objects.
|
|
31
|
+
* **Dictionary conversion:** Convert mapped objects back into standard Python dictionaries.
|
|
32
|
+
* **JSON serialization:** Serialize objects back into JSON.
|
|
33
|
+
* **Simple API:** Keep your code readable when working with complex JSON responses.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install jsonobjectify
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
Consider the following JSON data representing an employee and their department:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
response = """{
|
|
47
|
+
"id": 1,
|
|
48
|
+
"name": "Michael Anderson",
|
|
49
|
+
"email": "michael.anderson@example.com",
|
|
50
|
+
"website": "www.example.dev",
|
|
51
|
+
"address": {
|
|
52
|
+
"city": "Austin",
|
|
53
|
+
"state": "TX",
|
|
54
|
+
"country": "United States",
|
|
55
|
+
"geo_location": {"lat": "30.2672", "lng": "-97.7431"}
|
|
56
|
+
},
|
|
57
|
+
"skills": [
|
|
58
|
+
{"name": "Python", "level": "Advanced"},
|
|
59
|
+
{"name": "C", "level": "Advanced"},
|
|
60
|
+
{"name": "C++", "level": "Intermediate"}
|
|
61
|
+
]
|
|
62
|
+
}"""
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
With `jsonobjectify`, you can access the data through object attributes rather than
|
|
66
|
+
repeated dictionary lookups.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from jsonobjectify import JsonPath
|
|
70
|
+
|
|
71
|
+
employee = JsonPath.from_json_string(response)
|
|
72
|
+
|
|
73
|
+
print(employee.name)
|
|
74
|
+
print(employee.address.city)
|
|
75
|
+
print(employee.skills[0].name)
|
|
76
|
+
print(employee.skills[1].level)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Output**
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
'Michael Anderson'
|
|
83
|
+
'Austin'
|
|
84
|
+
'Python'
|
|
85
|
+
'Advanced'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The example above illustrates the intended API. Adjust the import and construction methods
|
|
89
|
+
to match the implementation in your repository.
|
|
90
|
+
|
|
91
|
+
## Converting Objects Back to JSON
|
|
92
|
+
|
|
93
|
+
`jsonobjectify` is intended to support conversion back to standard Python dictionaries and
|
|
94
|
+
JSON strings, making it convenient to work with transformed data.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# Convert the object to a Python dictionary
|
|
98
|
+
data = employee.to_dict()
|
|
99
|
+
|
|
100
|
+
# Serialize the object to a JSON string
|
|
101
|
+
json_data = employee.to_json()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Requirements
|
|
105
|
+
|
|
106
|
+
* Python 3.9 or later
|
|
107
|
+
|
|
108
|
+
## Contributing
|
|
109
|
+
|
|
110
|
+
Contributions, bug reports, and feature suggestions are welcome. Please open an issue to
|
|
111
|
+
discuss a proposed change before submitting a pull request.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# jsonobjectify
|
|
2
|
+
|
|
3
|
+
A lightweight Python library for converting JSON data into Python objects,
|
|
4
|
+
simplifying access to nested structures through intuitive dot notation.
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
Working with complex JSON data often involves repetitive dictionary lookups, especially
|
|
8
|
+
when accessing deeply nested structures. `jsonobjectify` simplifies this process by
|
|
9
|
+
converting JSON data into Python objects, allowing you to access JSON properties using
|
|
10
|
+
familiar attribute notation.
|
|
11
|
+
|
|
12
|
+
Instead of writing chained dictionary lookups, you can navigate nested JSON structures
|
|
13
|
+
using a clean, readable, object-oriented interface.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
* **Dot-notation access:** Access JSON properties using Python attributes.
|
|
18
|
+
* **Nested object support:** Navigate deeply nested JSON structures naturally.
|
|
19
|
+
* **List support:** Work with collections of JSON objects.
|
|
20
|
+
* **Dictionary conversion:** Convert mapped objects back into standard Python dictionaries.
|
|
21
|
+
* **JSON serialization:** Serialize objects back into JSON.
|
|
22
|
+
* **Simple API:** Keep your code readable when working with complex JSON responses.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install jsonobjectify
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Consider the following JSON data representing an employee and their department:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
response = """{
|
|
36
|
+
"id": 1,
|
|
37
|
+
"name": "Michael Anderson",
|
|
38
|
+
"email": "michael.anderson@example.com",
|
|
39
|
+
"website": "www.example.dev",
|
|
40
|
+
"address": {
|
|
41
|
+
"city": "Austin",
|
|
42
|
+
"state": "TX",
|
|
43
|
+
"country": "United States",
|
|
44
|
+
"geo_location": {"lat": "30.2672", "lng": "-97.7431"}
|
|
45
|
+
},
|
|
46
|
+
"skills": [
|
|
47
|
+
{"name": "Python", "level": "Advanced"},
|
|
48
|
+
{"name": "C", "level": "Advanced"},
|
|
49
|
+
{"name": "C++", "level": "Intermediate"}
|
|
50
|
+
]
|
|
51
|
+
}"""
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
With `jsonobjectify`, you can access the data through object attributes rather than
|
|
55
|
+
repeated dictionary lookups.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from jsonobjectify import JsonPath
|
|
59
|
+
|
|
60
|
+
employee = JsonPath.from_json_string(response)
|
|
61
|
+
|
|
62
|
+
print(employee.name)
|
|
63
|
+
print(employee.address.city)
|
|
64
|
+
print(employee.skills[0].name)
|
|
65
|
+
print(employee.skills[1].level)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Output**
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
'Michael Anderson'
|
|
72
|
+
'Austin'
|
|
73
|
+
'Python'
|
|
74
|
+
'Advanced'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The example above illustrates the intended API. Adjust the import and construction methods
|
|
78
|
+
to match the implementation in your repository.
|
|
79
|
+
|
|
80
|
+
## Converting Objects Back to JSON
|
|
81
|
+
|
|
82
|
+
`jsonobjectify` is intended to support conversion back to standard Python dictionaries and
|
|
83
|
+
JSON strings, making it convenient to work with transformed data.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# Convert the object to a Python dictionary
|
|
87
|
+
data = employee.to_dict()
|
|
88
|
+
|
|
89
|
+
# Serialize the object to a JSON string
|
|
90
|
+
json_data = employee.to_json()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
* Python 3.9 or later
|
|
96
|
+
|
|
97
|
+
## Contributing
|
|
98
|
+
|
|
99
|
+
Contributions, bug reports, and feature suggestions are welcome. Please open an issue to
|
|
100
|
+
discuss a proposed change before submitting a pull request.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "jsonobjectify"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "A lightweight Python library for converting JSON data into Python objects, simplifying access to nested structures through intuitive dot notation."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[[project.authors]]
|
|
10
|
+
name = "Sandeep Suryaprasad"
|
|
11
|
+
|
|
12
|
+
[project.urls]
|
|
13
|
+
Homepage = "https://github.com/sandeepsuryaprasad/jsonobjectify"
|
|
14
|
+
Repository = "https://github.com/sandeepsuryaprasad/jsonobjectify"
|
|
15
|
+
Issues = "https://github.com/sandeepsuryaprasad/jsonobjectify/issues"
|
|
16
|
+
|
|
17
|
+
[tool.uv.build-backend]
|
|
18
|
+
module-root = "src"
|
|
19
|
+
module-name = "jsonobjectify"
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.ruff]
|
|
32
|
+
line-length = 119
|
|
33
|
+
exclude = [
|
|
34
|
+
"tests",
|
|
35
|
+
"sitepackages",
|
|
36
|
+
".git",
|
|
37
|
+
".github",
|
|
38
|
+
"__pycache__",
|
|
39
|
+
".venv",
|
|
40
|
+
"build",
|
|
41
|
+
"dist",
|
|
42
|
+
".pytest_cache",
|
|
43
|
+
".tox",
|
|
44
|
+
".vscode",
|
|
45
|
+
".ruff_cache",
|
|
46
|
+
".mypy_cache",
|
|
47
|
+
".nox",
|
|
48
|
+
".eggs",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = [
|
|
53
|
+
"F",
|
|
54
|
+
"N",
|
|
55
|
+
"E9",
|
|
56
|
+
"I",
|
|
57
|
+
"E501",
|
|
58
|
+
"RUF022",
|
|
59
|
+
"TID252",
|
|
60
|
+
"F841",
|
|
61
|
+
"N816",
|
|
62
|
+
"B007",
|
|
63
|
+
"D403",
|
|
64
|
+
"D101",
|
|
65
|
+
"D102",
|
|
66
|
+
"D103",
|
|
67
|
+
"D106",
|
|
68
|
+
"D201",
|
|
69
|
+
"D202",
|
|
70
|
+
"D204",
|
|
71
|
+
"D300",
|
|
72
|
+
"D404",
|
|
73
|
+
"D405",
|
|
74
|
+
"D419",
|
|
75
|
+
"E402",
|
|
76
|
+
"E401",
|
|
77
|
+
"E711",
|
|
78
|
+
"E712",
|
|
79
|
+
"E721",
|
|
80
|
+
"E722",
|
|
81
|
+
"E731",
|
|
82
|
+
]
|
|
83
|
+
ignore = [
|
|
84
|
+
"D100",
|
|
85
|
+
"D101",
|
|
86
|
+
"D104",
|
|
87
|
+
"D105",
|
|
88
|
+
"D107",
|
|
89
|
+
"D212",
|
|
90
|
+
"D415",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint.pydocstyle]
|
|
94
|
+
convention = "google"
|
|
95
|
+
|
|
96
|
+
[tool.pytest.ini_options]
|
|
97
|
+
testpaths = ["./tests"]
|
|
98
|
+
|
|
99
|
+
[dependency-groups]
|
|
100
|
+
test = ["pytest==8.2.2"]
|
|
101
|
+
lint = ["ruff==0.11.12"]
|
|
102
|
+
|
|
103
|
+
[build-system]
|
|
104
|
+
requires = ["uv_build>=0.9.13,<0.12.0"]
|
|
105
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "jsonobjectify"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "A lightweight Python library for converting JSON data into Python objects, simplifying access to nested structures through intuitive dot notation."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
|
+
dependencies = []
|
|
8
|
+
authors = [
|
|
9
|
+
{name= "Sandeep Suryaprasad"}
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[tool.uv.build-backend]
|
|
13
|
+
module-root = "src"
|
|
14
|
+
module-name = "jsonobjectify"
|
|
15
|
+
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/sandeepsuryaprasad/jsonobjectify"
|
|
29
|
+
Repository = "https://github.com/sandeepsuryaprasad/jsonobjectify"
|
|
30
|
+
Issues = "https://github.com/sandeepsuryaprasad/jsonobjectify/issues"
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
test = ["pytest==8.2.2"]
|
|
34
|
+
lint = ["ruff==0.11.12"]
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 119
|
|
38
|
+
exclude = [
|
|
39
|
+
"tests",
|
|
40
|
+
"sitepackages",
|
|
41
|
+
".git",
|
|
42
|
+
".github",
|
|
43
|
+
"__pycache__",
|
|
44
|
+
".venv",
|
|
45
|
+
"build",
|
|
46
|
+
"dist",
|
|
47
|
+
".pytest_cache",
|
|
48
|
+
".tox",
|
|
49
|
+
".vscode",
|
|
50
|
+
".ruff_cache",
|
|
51
|
+
".mypy_cache",
|
|
52
|
+
".nox",
|
|
53
|
+
".eggs"
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = [
|
|
58
|
+
"F",
|
|
59
|
+
"N",
|
|
60
|
+
"E9", # Checks for common syntax errors, indendation errors
|
|
61
|
+
"I",
|
|
62
|
+
"E501", # Checks for lines that exceed the specified maximum character length.
|
|
63
|
+
"RUF022",
|
|
64
|
+
"TID252",
|
|
65
|
+
"F841", # Checks for the presence of unused variables in function scopes.
|
|
66
|
+
"N816", # Checks for global variable names that follow the mixedCase convention.
|
|
67
|
+
"B007", # Checks for unused variables in loops (e.g., for and while statements).
|
|
68
|
+
"D403", # Checks for docstrings that do not start with a capital letter.
|
|
69
|
+
"D101",
|
|
70
|
+
"D102", # Checks for undocumented public method definitions.
|
|
71
|
+
"D103", # Checks for undocumented public function definitions.
|
|
72
|
+
"D106", # Checks for undocumented public class definitions, for nested classes.
|
|
73
|
+
"D201", # Checks for docstrings on functions that are separated by one or more blank lines from the function definition.
|
|
74
|
+
"D202", # Checks for docstrings on functions that are separated by one or more blank lines from the function body.
|
|
75
|
+
"D204", # Checks for class methods that are not separated from the class's docstring by a blank line.
|
|
76
|
+
"D300", # Checks for docstrings that use '''triple single quotes''' instead of """triple double quotes""".
|
|
77
|
+
"D404", # Checks for docstrings that start with This.
|
|
78
|
+
"D405", # Checks for section headers in docstrings that do not begin with capital letters.
|
|
79
|
+
"D419", # Checks for empty docstrings.
|
|
80
|
+
"E402", # Checks for imports that are not at the top of the file.
|
|
81
|
+
"E401", # Check for multiple imports on one line.
|
|
82
|
+
"E711", # Checks for comparisons to None which are not using the is operator.
|
|
83
|
+
"E712", # Checks for equality comparisons to boolean literals.
|
|
84
|
+
"E721", # Checks for object type comparisons using == and other comparison operators.
|
|
85
|
+
"E722", # Checks for bare except catches in try-except statements.
|
|
86
|
+
"E731", # Checks for lambda expressions which are assigned to a variable.
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
ignore = [
|
|
90
|
+
"D100", # Missing docstring in public module
|
|
91
|
+
"D101", # Missing docstring in public class
|
|
92
|
+
"D104", # Missing docstring in public package
|
|
93
|
+
"D105", # Missing docstring in magic method
|
|
94
|
+
"D107", # Missing docstring in `__init__`
|
|
95
|
+
"D212", # Multi-line docstring summary should start at the first line
|
|
96
|
+
"D415" # Ignore if the doc string does not end with any punctuation
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint.pydocstyle]
|
|
100
|
+
convention = "google"
|
|
101
|
+
|
|
102
|
+
[tool.pytest.ini_options]
|
|
103
|
+
testpaths = ["./tests"]
|
|
104
|
+
|
|
105
|
+
[build-system]
|
|
106
|
+
requires = ["uv_build>=0.9.13,<0.12.0"]
|
|
107
|
+
build-backend = "uv_build"
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from json import dumps
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class JsonObject:
|
|
5
|
+
"""Represent a JSON object as a Python object."""
|
|
6
|
+
def __init__(self, info):
|
|
7
|
+
self._info = info
|
|
8
|
+
|
|
9
|
+
def __getattr__(self, name):
|
|
10
|
+
"""Return a value from the underlying JSON data using dot notation."""
|
|
11
|
+
try:
|
|
12
|
+
return self._info[name]
|
|
13
|
+
except KeyError:
|
|
14
|
+
raise AttributeError(f"{type(self).__name__} has no attribute {name!r}") from None
|
|
15
|
+
|
|
16
|
+
def __setattr__(self, name, value):
|
|
17
|
+
"""Set an attribute (_info) or update the underlying JSON data."""
|
|
18
|
+
if name == "_info":
|
|
19
|
+
super().__setattr__(name, value)
|
|
20
|
+
else:
|
|
21
|
+
self._info[name] = value
|
|
22
|
+
|
|
23
|
+
def to_dict(self):
|
|
24
|
+
"""Convert a JsonObject to dictionary."""
|
|
25
|
+
out_dict = {}
|
|
26
|
+
for key, value in self._info.items():
|
|
27
|
+
if isinstance(value, JsonObject):
|
|
28
|
+
out_dict[key] = value.to_dict()
|
|
29
|
+
elif isinstance(value, list):
|
|
30
|
+
out_dict[key] = [
|
|
31
|
+
item.to_dict() if isinstance(item, JsonObject) else item
|
|
32
|
+
for item in value
|
|
33
|
+
]
|
|
34
|
+
else:
|
|
35
|
+
out_dict[key] = value
|
|
36
|
+
return out_dict
|
|
37
|
+
|
|
38
|
+
def to_json(self):
|
|
39
|
+
"""Convert JsonObject to a JSON string."""
|
|
40
|
+
return dumps(self.to_dict())
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from json import load, loads
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from .json_object import JsonObject
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class JsonPath:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
"""Initialize an empty JSON parser."""
|
|
9
|
+
self._root_json_object = None
|
|
10
|
+
|
|
11
|
+
def __getattr__(self, name):
|
|
12
|
+
"""Delegate attribute access to the json object."""
|
|
13
|
+
return getattr(self._root_json_object, name)
|
|
14
|
+
|
|
15
|
+
def __getitem__(self, index):
|
|
16
|
+
"""Return an item from the root JSON array."""
|
|
17
|
+
return self._root_json_object[index]
|
|
18
|
+
|
|
19
|
+
def __len__(self):
|
|
20
|
+
"""Return the length of root JSON array/object"""
|
|
21
|
+
if isinstance(self._root_json_object, JsonObject):
|
|
22
|
+
return len(self._root_json_object.to_dict())
|
|
23
|
+
return len(self._root_json_object)
|
|
24
|
+
|
|
25
|
+
def __setattr__(self, name, value):
|
|
26
|
+
if name == "_root_json_object":
|
|
27
|
+
super().__setattr__(name, value)
|
|
28
|
+
else:
|
|
29
|
+
setattr(self._root_json_object, name, value)
|
|
30
|
+
|
|
31
|
+
def __str__(self):
|
|
32
|
+
if isinstance(self._root_json_object, JsonObject):
|
|
33
|
+
return self._root_json_object.to_json()
|
|
34
|
+
return f"{self._root_json_object}"
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
if isinstance(self._root_json_object, JsonObject):
|
|
38
|
+
return f"{type(self).__name__}({self._root_json_object.to_dict()})"
|
|
39
|
+
return f"{type(self).__name__}({self._root_json_object})"
|
|
40
|
+
|
|
41
|
+
def _wrap_dict(self, data):
|
|
42
|
+
"""Wrap a dictionary in a JsonObject."""
|
|
43
|
+
return JsonObject(data)
|
|
44
|
+
|
|
45
|
+
def _is_valid_json_key(self, key):
|
|
46
|
+
"""Check whether a JSON key can be used as a Python attribute."""
|
|
47
|
+
if key.isidentifier():
|
|
48
|
+
return True
|
|
49
|
+
else:
|
|
50
|
+
print(f"Skipping invalid JSON key {key!r}")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def _process_list(self, data: list):
|
|
54
|
+
"""Recursively process items in a list."""
|
|
55
|
+
return [self._process_object(item) for item in data]
|
|
56
|
+
|
|
57
|
+
def _process_dict(self, data: dict):
|
|
58
|
+
"""Recursively process values in a dictionary."""
|
|
59
|
+
_data = {
|
|
60
|
+
key: self._process_object(value)
|
|
61
|
+
for key, value in data.items()
|
|
62
|
+
if self._is_valid_json_key(key)
|
|
63
|
+
}
|
|
64
|
+
return self._wrap_dict(_data)
|
|
65
|
+
|
|
66
|
+
def _process_object(self, value):
|
|
67
|
+
"""Process a JSON object based on its Python type."""
|
|
68
|
+
if isinstance(value, dict):
|
|
69
|
+
return self._process_dict(value)
|
|
70
|
+
elif isinstance(value, list):
|
|
71
|
+
return self._process_list(value)
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
def _build_root_object(self, py_object):
|
|
75
|
+
"""Build the root object from the deserialized JSON."""
|
|
76
|
+
return self._process_object(py_object)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def _from_py_object(cls, py_object):
|
|
80
|
+
obj = cls()
|
|
81
|
+
obj._root_json_object = obj._build_root_object(py_object)
|
|
82
|
+
return obj
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def from_json_string(cls, json_string):
|
|
86
|
+
"""Create a JsonPath instance from a JSON string."""
|
|
87
|
+
if not isinstance(json_string, str):
|
|
88
|
+
raise TypeError("json_string must be a string")
|
|
89
|
+
if not json_string.strip():
|
|
90
|
+
raise ValueError("JSON string cannot be empty")
|
|
91
|
+
return cls._from_py_object(loads(json_string))
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def from_json_file(cls, filename: str):
|
|
95
|
+
"""Create a JsonPath instance from a JSON file."""
|
|
96
|
+
path = Path(filename)
|
|
97
|
+
if not path.is_file():
|
|
98
|
+
raise FileNotFoundError(f"File not found {filename}")
|
|
99
|
+
with open(path, mode="r", encoding="utf-8") as json_file:
|
|
100
|
+
return cls._from_py_object(load(json_file))
|