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.
@@ -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))