jsonxox 0.8.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,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2023–2026 Daniel Sissman.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
jsonxox-0.8.0/PKG-INFO ADDED
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonxox
3
+ Version: 0.8.0
4
+ Summary: Parsing for extended JSON with optional comments and trailing commas
5
+ Author: Daniel Sissman
6
+ License-Expression: MIT
7
+ Project-URL: documentation, https://github.com/bluebinary/jsonxox/blob/main/README.md
8
+ Project-URL: changelog, https://github.com/bluebinary/jsonxox/blob/main/CHANGELOG.md
9
+ Project-URL: repository, https://github.com/bluebinary/jsonxox
10
+ Project-URL: issues, https://github.com/bluebinary/jsonxox/issues
11
+ Project-URL: homepage, https://github.com/bluebinary/jsonxox
12
+ Keywords: json,extened json,json with comments,json with trailing commas
13
+ Platform: any
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE.md
23
+ Provides-Extra: development
24
+ Requires-Dist: black==26.1.*; extra == "development"
25
+ Requires-Dist: pytest==9.0.*; extra == "development"
26
+ Requires-Dist: pytest-codeblocks==0.17.*; extra == "development"
27
+ Requires-Dist: pyflakes==3.4.*; extra == "development"
28
+ Provides-Extra: distribution
29
+ Requires-Dist: build; extra == "distribution"
30
+ Requires-Dist: twine; extra == "distribution"
31
+ Requires-Dist: wheel; extra == "distribution"
32
+ Dynamic: license-file
33
+
34
+ # JSONXOX: Extended JSON Parsing
35
+
36
+ Do you love the JSON serialization format but wish it included support for optional
37
+ comments and trailing commas? Well if so, the JSONXOX library is for you.
38
+
39
+ The JSONXOX library provides support for parsing extended JSON files and strings – JSON
40
+ files and strings which may contain optional C-style single-line and multi-line comments
41
+ as well as trailing commas.
42
+
43
+ ### Requirements
44
+
45
+ The JSONXOX library has been tested with Python 3.10, 3.11, 3.12, 3.13 and 3.14. The
46
+ library has not been tested with, nor is it likely compatible with Python 3.9 and earlier.
47
+
48
+ ### Installation
49
+
50
+ The JSONXOX library is available from PyPI, so may be added to a project's dependencies
51
+ via its `requirements.txt` file or similar by referencing the JSONXOX library's name,
52
+ `jsonxox`, or the library may be installed directly into the local runtime environment
53
+ using `pip install` by running the following command:
54
+
55
+ $ pip install jsonxox
56
+
57
+ ### Usage Example
58
+
59
+ To use the JSONXOX library, simply import the library into your project as a drop-in
60
+ replacement for the standard `json` library, and use it as you would to load a JSON file
61
+ or JSON string. The library can be used to load standard JSON files and strings as well
62
+ as extended JSON files and strings containing single- and multi-line comments as well as
63
+ trailing commas. The library can also be used to save standard JSON files and strings.
64
+
65
+ See the [**Methods**](#methods) section for more information about the methods provided
66
+ by the library.
67
+
68
+ ```python
69
+ import jsonxox
70
+
71
+ sample: str = """
72
+ /* this is a multi-line comment block
73
+ that can span multiple lines or just a single line
74
+ */
75
+ {
76
+ "a": 123, /* this is also a multi-line comment block (using a single line) */
77
+ // this is a single-line comment
78
+ "b": "456", // notice the trailing comma
79
+ }
80
+ """
81
+
82
+ data = jsonxox.loads(sample)
83
+
84
+ assert isinstance(data, dict)
85
+
86
+ assert "a" in data
87
+ assert data["a"] == 123
88
+
89
+ assert "b" in data
90
+ assert data["b"] == "456"
91
+ ```
92
+
93
+ <a id="methods"></a>
94
+ ### Methods
95
+
96
+ The `jsonxox` library provides the following methods, mirroring the standard `json`
97
+ library in its available methods and overall functionality:
98
+
99
+ * `load(handle, **kwargs)` (`object`) – The `load()` method takes a file handle or file
100
+ handle like object that is an instance of `io.TextIOWrapper` or one of its subclasses
101
+ as input and removes any single and multi-line comments as well as any trailing commas
102
+ from the input string contents read from the file handle, and then passes the cleaned
103
+ string to the standard library's `json.loads()` method for quick deserialization to its
104
+ corresponding Python data types. Any additional keyword arguments are passed to the
105
+ `json.loads()` method, as such any additional keyword arguments must be compatible with
106
+ the `json.loads()` method.
107
+
108
+ * `loads(handle, **kwargs)` (`object`) – The `loads()` method takes a string as input
109
+ and removes any single and multi-line comments as well as any trailing commas from the
110
+ input string, and then passes the cleaned string to the standard library's `json.loads()`
111
+ method for quick deserialization to its corresponding Python data types. Any additional
112
+ keyword arguments are passed to the `json.loads()` method, as such any additional
113
+ keyword arguments must be compatible with the `json.loads()` method
114
+
115
+ * `dump(*args, **kwargs)` – The `dump()` method is a passthrough to the standard
116
+ `json` library's `dump()` method and accepts the same arguments and has the same return
117
+ values. Note that the `dump()` method cannot be used to re-save any comments or trailing
118
+ commas that may have been present in the original extended JSON file or string.
119
+
120
+ * `dumps(*args, **kwargs)` (`str`) – The `dumps()` method is a passthrough to the standard
121
+ `json` library's `dumps()` method and accepts the same arguments and has the same return
122
+ values. Note that the `dumps()` method cannot be used to re-save any comments or trailing
123
+ commas that may have been present in the original extended JSON file or string.
124
+
125
+ ### Classes
126
+
127
+ The `jsonxox` library provides the following classes, mirroring the standard `json`
128
+ library in its available classes:
129
+
130
+ * `JSONDecoder` – The `JSONDecoder` class is a direct import of and reference to the
131
+ standard `json` library's `JSONDecoder` class.
132
+
133
+ * `JSONEncoder` – The `JSONEncoder` class is a direct import of and reference to the
134
+ standard `json` library's `JSONEncoder` class.
135
+
136
+ ### Exceptions
137
+
138
+ The `jsonxox` library provides the following exception classes, mirroring the standard
139
+ `json` library in its available exception classes:
140
+
141
+ * `JSONDecodeError` – The `JSONDecodeError` exception class is a direct import of and
142
+ reference to the standard `json` library's `JSONDecodeError` exception class.
143
+
144
+ * `JSONXOXDecodeError` – The `JSONXOXDecodeError` exception class is a subclass of the
145
+ standard `json` library's `JSONDecodeError` class and will be raised by the `jsonxox`
146
+ library if an exception occurs while attempting to clean or load the JSON input.
147
+
148
+ ### Unit Tests
149
+
150
+ The JSONXOX library includes a suite of comprehensive unit tests which ensure that the
151
+ library functionality operates as expected. The unit tests were developed with and are
152
+ run via `pytest`.
153
+
154
+ To ensure that the unit tests are run within a predictable runtime environment where all
155
+ of the necessary dependencies are available, a [Docker](https://www.docker.com) image is
156
+ created within which the tests are run. To run the unit tests, ensure Docker and Docker
157
+ Compose is [installed](https://docs.docker.com/engine/install/), and perform the
158
+ following commands, which will build the Docker image via `docker compose build` and
159
+ then run the tests via `docker compose run` – the output the tests will be displayed:
160
+
161
+ ```shell
162
+ $ docker compose build
163
+ $ docker compose run tests
164
+ ```
165
+
166
+ To run the unit tests with optional command line arguments being passed to `pytest`,
167
+ append the relevant arguments to the `docker compose run tests` command, as follows, for
168
+ example passing `-v` to enable verbose output and `-s` to print standard output:
169
+
170
+ ```shell
171
+ $ docker compose run tests -v -s
172
+ ```
173
+
174
+ See the documentation for [PyTest](https://docs.pytest.org/en/latest/) regarding
175
+ available optional command line arguments.
176
+
177
+ ### Copyright & License Information
178
+
179
+ Copyright © 2023–2026 Daniel Sissman; licensed under the MIT License.
@@ -0,0 +1,146 @@
1
+ # JSONXOX: Extended JSON Parsing
2
+
3
+ Do you love the JSON serialization format but wish it included support for optional
4
+ comments and trailing commas? Well if so, the JSONXOX library is for you.
5
+
6
+ The JSONXOX library provides support for parsing extended JSON files and strings – JSON
7
+ files and strings which may contain optional C-style single-line and multi-line comments
8
+ as well as trailing commas.
9
+
10
+ ### Requirements
11
+
12
+ The JSONXOX library has been tested with Python 3.10, 3.11, 3.12, 3.13 and 3.14. The
13
+ library has not been tested with, nor is it likely compatible with Python 3.9 and earlier.
14
+
15
+ ### Installation
16
+
17
+ The JSONXOX library is available from PyPI, so may be added to a project's dependencies
18
+ via its `requirements.txt` file or similar by referencing the JSONXOX library's name,
19
+ `jsonxox`, or the library may be installed directly into the local runtime environment
20
+ using `pip install` by running the following command:
21
+
22
+ $ pip install jsonxox
23
+
24
+ ### Usage Example
25
+
26
+ To use the JSONXOX library, simply import the library into your project as a drop-in
27
+ replacement for the standard `json` library, and use it as you would to load a JSON file
28
+ or JSON string. The library can be used to load standard JSON files and strings as well
29
+ as extended JSON files and strings containing single- and multi-line comments as well as
30
+ trailing commas. The library can also be used to save standard JSON files and strings.
31
+
32
+ See the [**Methods**](#methods) section for more information about the methods provided
33
+ by the library.
34
+
35
+ ```python
36
+ import jsonxox
37
+
38
+ sample: str = """
39
+ /* this is a multi-line comment block
40
+ that can span multiple lines or just a single line
41
+ */
42
+ {
43
+ "a": 123, /* this is also a multi-line comment block (using a single line) */
44
+ // this is a single-line comment
45
+ "b": "456", // notice the trailing comma
46
+ }
47
+ """
48
+
49
+ data = jsonxox.loads(sample)
50
+
51
+ assert isinstance(data, dict)
52
+
53
+ assert "a" in data
54
+ assert data["a"] == 123
55
+
56
+ assert "b" in data
57
+ assert data["b"] == "456"
58
+ ```
59
+
60
+ <a id="methods"></a>
61
+ ### Methods
62
+
63
+ The `jsonxox` library provides the following methods, mirroring the standard `json`
64
+ library in its available methods and overall functionality:
65
+
66
+ * `load(handle, **kwargs)` (`object`) – The `load()` method takes a file handle or file
67
+ handle like object that is an instance of `io.TextIOWrapper` or one of its subclasses
68
+ as input and removes any single and multi-line comments as well as any trailing commas
69
+ from the input string contents read from the file handle, and then passes the cleaned
70
+ string to the standard library's `json.loads()` method for quick deserialization to its
71
+ corresponding Python data types. Any additional keyword arguments are passed to the
72
+ `json.loads()` method, as such any additional keyword arguments must be compatible with
73
+ the `json.loads()` method.
74
+
75
+ * `loads(handle, **kwargs)` (`object`) – The `loads()` method takes a string as input
76
+ and removes any single and multi-line comments as well as any trailing commas from the
77
+ input string, and then passes the cleaned string to the standard library's `json.loads()`
78
+ method for quick deserialization to its corresponding Python data types. Any additional
79
+ keyword arguments are passed to the `json.loads()` method, as such any additional
80
+ keyword arguments must be compatible with the `json.loads()` method
81
+
82
+ * `dump(*args, **kwargs)` – The `dump()` method is a passthrough to the standard
83
+ `json` library's `dump()` method and accepts the same arguments and has the same return
84
+ values. Note that the `dump()` method cannot be used to re-save any comments or trailing
85
+ commas that may have been present in the original extended JSON file or string.
86
+
87
+ * `dumps(*args, **kwargs)` (`str`) – The `dumps()` method is a passthrough to the standard
88
+ `json` library's `dumps()` method and accepts the same arguments and has the same return
89
+ values. Note that the `dumps()` method cannot be used to re-save any comments or trailing
90
+ commas that may have been present in the original extended JSON file or string.
91
+
92
+ ### Classes
93
+
94
+ The `jsonxox` library provides the following classes, mirroring the standard `json`
95
+ library in its available classes:
96
+
97
+ * `JSONDecoder` – The `JSONDecoder` class is a direct import of and reference to the
98
+ standard `json` library's `JSONDecoder` class.
99
+
100
+ * `JSONEncoder` – The `JSONEncoder` class is a direct import of and reference to the
101
+ standard `json` library's `JSONEncoder` class.
102
+
103
+ ### Exceptions
104
+
105
+ The `jsonxox` library provides the following exception classes, mirroring the standard
106
+ `json` library in its available exception classes:
107
+
108
+ * `JSONDecodeError` – The `JSONDecodeError` exception class is a direct import of and
109
+ reference to the standard `json` library's `JSONDecodeError` exception class.
110
+
111
+ * `JSONXOXDecodeError` – The `JSONXOXDecodeError` exception class is a subclass of the
112
+ standard `json` library's `JSONDecodeError` class and will be raised by the `jsonxox`
113
+ library if an exception occurs while attempting to clean or load the JSON input.
114
+
115
+ ### Unit Tests
116
+
117
+ The JSONXOX library includes a suite of comprehensive unit tests which ensure that the
118
+ library functionality operates as expected. The unit tests were developed with and are
119
+ run via `pytest`.
120
+
121
+ To ensure that the unit tests are run within a predictable runtime environment where all
122
+ of the necessary dependencies are available, a [Docker](https://www.docker.com) image is
123
+ created within which the tests are run. To run the unit tests, ensure Docker and Docker
124
+ Compose is [installed](https://docs.docker.com/engine/install/), and perform the
125
+ following commands, which will build the Docker image via `docker compose build` and
126
+ then run the tests via `docker compose run` – the output the tests will be displayed:
127
+
128
+ ```shell
129
+ $ docker compose build
130
+ $ docker compose run tests
131
+ ```
132
+
133
+ To run the unit tests with optional command line arguments being passed to `pytest`,
134
+ append the relevant arguments to the `docker compose run tests` command, as follows, for
135
+ example passing `-v` to enable verbose output and `-s` to print standard output:
136
+
137
+ ```shell
138
+ $ docker compose run tests -v -s
139
+ ```
140
+
141
+ See the documentation for [PyTest](https://docs.pytest.org/en/latest/) regarding
142
+ available optional command line arguments.
143
+
144
+ ### Copyright & License Information
145
+
146
+ Copyright © 2023–2026 Daniel Sissman; licensed under the MIT License.
@@ -0,0 +1,77 @@
1
+ [project]
2
+ name = "jsonxox"
3
+ description = "Parsing for extended JSON with optional comments and trailing commas"
4
+ readme = {file = "README.md", content-type = "text/markdown"}
5
+ keywords = ["json", "extened json", "json with comments", "json with trailing commas"]
6
+ authors = [{name = "Daniel Sissman"}]
7
+ license = "MIT"
8
+ classifiers = [
9
+ "Programming Language :: Python :: 3",
10
+ "Programming Language :: Python :: 3.10",
11
+ "Programming Language :: Python :: 3.11",
12
+ "Programming Language :: Python :: 3.12",
13
+ "Programming Language :: Python :: 3.13",
14
+ "Programming Language :: Python :: 3.14",
15
+ ]
16
+ requires-python = ">=3.10"
17
+ dynamic = [
18
+ "version",
19
+ "dependencies",
20
+ "optional-dependencies",
21
+ ]
22
+
23
+ [project.urls]
24
+ documentation = "https://github.com/bluebinary/jsonxox/blob/main/README.md"
25
+ changelog = "https://github.com/bluebinary/jsonxox/blob/main/CHANGELOG.md"
26
+ repository = "https://github.com/bluebinary/jsonxox"
27
+ issues = "https://github.com/bluebinary/jsonxox/issues"
28
+ homepage = "https://github.com/bluebinary/jsonxox"
29
+
30
+ [build-system]
31
+ requires = ["setuptools", "wheel"]
32
+ build-backend = "setuptools.build_meta"
33
+
34
+ [tool.setuptools.dynamic]
35
+ version = {file = "source/jsonxox/version.txt"}
36
+ dependencies = {file = "requirements.txt"}
37
+
38
+ [tool.setuptools.dynamic.optional-dependencies]
39
+ development = {file = "requirements.development.txt"}
40
+ distribution = {file = "requirements.distribution.txt"}
41
+
42
+ [tool.setuptools]
43
+ platforms = ["any"]
44
+ zip-safe = true
45
+ include-package-data = true
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["source"]
49
+ exclude = ["resources*", "tests*", "*#*"]
50
+
51
+ [tool.setuptools.package-data]
52
+ "*" = ["*.json", "*.jsonx", "*.jsonc"]
53
+ source = ["*.json", "*.jsonx", "*.jsonc"]
54
+ tests = ["tests/data/*.json", "tests/data/*.jsonx", "tests/data/*.jsonc"]
55
+
56
+ [tool.setuptools.exclude-package-data]
57
+ "*" = ["*#*", "#*", "@*"]
58
+
59
+ [tool.pytest.ini_options]
60
+ minversion = "6.0"
61
+ addopts = "-ra -q"
62
+ testpaths = [
63
+ "tests"
64
+ ]
65
+
66
+ [tool.black]
67
+ line-length = 88
68
+ target-version = ["py310"]
69
+ include = '\.pyi?$'
70
+ extend-exclude = '''
71
+ /(
72
+ # The following are specific to Black, you probably don't want those.
73
+ | blib2to3
74
+ | tests/data
75
+ | profiling
76
+ )/
77
+ '''
@@ -0,0 +1,5 @@
1
+ # JSONXOX Library: Development & Test Dependencies
2
+ black==26.1.*
3
+ pytest==9.0.*
4
+ pytest-codeblocks==0.17.*
5
+ pyflakes==3.4.*
@@ -0,0 +1,4 @@
1
+ # JSONXOX Library: Build & Deployment Dependencies
2
+ build
3
+ twine
4
+ wheel
@@ -0,0 +1 @@
1
+ # JSONXOX Library: Runtime Dependencies
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,103 @@
1
+ import json
2
+ import re
3
+ import io
4
+
5
+ from json import (
6
+ JSONDecoder,
7
+ JSONEncoder,
8
+ JSONDecodeError,
9
+ )
10
+
11
+
12
+ class JSONXOXDecodeError(JSONDecodeError):
13
+ pass
14
+
15
+
16
+ def _jsonx_to_json(content: str) -> str:
17
+ """With an extended JSON string as input (JSON that can contain C-style single and
18
+ multi-line comments as well as trailing commas) and return a standard JSON string
19
+ that has had these non-standard elements removed."""
20
+
21
+ if not isinstance(content, str):
22
+ raise TypeError("The 'content' argument must have a string value!")
23
+
24
+ if len(content) > 0:
25
+ flags = re.MULTILINE | re.DOTALL
26
+
27
+ try:
28
+ # Remove multi-line comments that begin with /* and end with */
29
+ content = re.sub(r"\/\*.*?\*\/", "", content, flags=flags)
30
+
31
+ # Remove single-line comments which begin with // and end at a newline, } or ]
32
+ content = re.sub(r"\s*(#|\/\/)([^\n\}\]]*)", "", content, flags=flags)
33
+
34
+ # Remove any trailing commas that are present before block closures
35
+ content = re.sub(r"\,(\s*)(\]|\})", r"\1\2", content, flags=flags)
36
+ except Exception as exception:
37
+ raise JSONXOXDecodeError(
38
+ "An exception occurred while cleaning the JSON: %s" % (str(exception))
39
+ ) from exception
40
+
41
+ return content.strip()
42
+
43
+
44
+ def load(handle, **kwargs) -> object | None:
45
+ """An extended json.load() method which supports loading JSON files with single and
46
+ multi-line comments as well as trailing commas."""
47
+
48
+ if not isinstance(handle, io.TextIOWrapper):
49
+ raise RuntimeError("The input must be compatible with io.TextIOWrapper!")
50
+
51
+ content = handle.read().strip()
52
+
53
+ if len(content) > 0:
54
+ if content := _jsonx_to_json(content):
55
+ try:
56
+ if isinstance(data := json.loads(content, **kwargs), object):
57
+ return data
58
+ except JSONDecodeError as exception:
59
+ raise JSONXOXDecodeError(str(exception)) from exception
60
+
61
+ return None
62
+
63
+
64
+ def loads(content: str, **kwargs) -> object | None:
65
+ """An extended json.loads() method which supports loading JSON files with single and
66
+ multi-line comments as well as trailing commas."""
67
+
68
+ if not isinstance(content, str):
69
+ raise TypeError("The 'content' argument must have a string value!")
70
+
71
+ if len(content) > 0:
72
+ if content := _jsonx_to_json(content):
73
+ try:
74
+ if isinstance(data := json.loads(content, **kwargs), object):
75
+ return data
76
+ except JSONDecodeError as exception:
77
+ raise JSONXOXDecodeError(str(exception)) from exception
78
+
79
+ return None
80
+
81
+
82
+ def dump(*args, **kwargs):
83
+ """A passthrough method for the json standard library's .dump() method."""
84
+
85
+ return json.dump(*args, **kwargs)
86
+
87
+
88
+ def dumps(*args, **kwargs):
89
+ """A passthrough method for the json standard library's .dumps() method."""
90
+
91
+ return json.dumps(*args, **kwargs)
92
+
93
+
94
+ __all__ = [
95
+ "load",
96
+ "loads",
97
+ "dump",
98
+ "dumps",
99
+ "JSONDecoder",
100
+ "JSONEncoder",
101
+ "JSONDecodeError",
102
+ "JSONXOXDecodeError",
103
+ ]
@@ -0,0 +1 @@
1
+ 0.8.0
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonxox
3
+ Version: 0.8.0
4
+ Summary: Parsing for extended JSON with optional comments and trailing commas
5
+ Author: Daniel Sissman
6
+ License-Expression: MIT
7
+ Project-URL: documentation, https://github.com/bluebinary/jsonxox/blob/main/README.md
8
+ Project-URL: changelog, https://github.com/bluebinary/jsonxox/blob/main/CHANGELOG.md
9
+ Project-URL: repository, https://github.com/bluebinary/jsonxox
10
+ Project-URL: issues, https://github.com/bluebinary/jsonxox/issues
11
+ Project-URL: homepage, https://github.com/bluebinary/jsonxox
12
+ Keywords: json,extened json,json with comments,json with trailing commas
13
+ Platform: any
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE.md
23
+ Provides-Extra: development
24
+ Requires-Dist: black==26.1.*; extra == "development"
25
+ Requires-Dist: pytest==9.0.*; extra == "development"
26
+ Requires-Dist: pytest-codeblocks==0.17.*; extra == "development"
27
+ Requires-Dist: pyflakes==3.4.*; extra == "development"
28
+ Provides-Extra: distribution
29
+ Requires-Dist: build; extra == "distribution"
30
+ Requires-Dist: twine; extra == "distribution"
31
+ Requires-Dist: wheel; extra == "distribution"
32
+ Dynamic: license-file
33
+
34
+ # JSONXOX: Extended JSON Parsing
35
+
36
+ Do you love the JSON serialization format but wish it included support for optional
37
+ comments and trailing commas? Well if so, the JSONXOX library is for you.
38
+
39
+ The JSONXOX library provides support for parsing extended JSON files and strings – JSON
40
+ files and strings which may contain optional C-style single-line and multi-line comments
41
+ as well as trailing commas.
42
+
43
+ ### Requirements
44
+
45
+ The JSONXOX library has been tested with Python 3.10, 3.11, 3.12, 3.13 and 3.14. The
46
+ library has not been tested with, nor is it likely compatible with Python 3.9 and earlier.
47
+
48
+ ### Installation
49
+
50
+ The JSONXOX library is available from PyPI, so may be added to a project's dependencies
51
+ via its `requirements.txt` file or similar by referencing the JSONXOX library's name,
52
+ `jsonxox`, or the library may be installed directly into the local runtime environment
53
+ using `pip install` by running the following command:
54
+
55
+ $ pip install jsonxox
56
+
57
+ ### Usage Example
58
+
59
+ To use the JSONXOX library, simply import the library into your project as a drop-in
60
+ replacement for the standard `json` library, and use it as you would to load a JSON file
61
+ or JSON string. The library can be used to load standard JSON files and strings as well
62
+ as extended JSON files and strings containing single- and multi-line comments as well as
63
+ trailing commas. The library can also be used to save standard JSON files and strings.
64
+
65
+ See the [**Methods**](#methods) section for more information about the methods provided
66
+ by the library.
67
+
68
+ ```python
69
+ import jsonxox
70
+
71
+ sample: str = """
72
+ /* this is a multi-line comment block
73
+ that can span multiple lines or just a single line
74
+ */
75
+ {
76
+ "a": 123, /* this is also a multi-line comment block (using a single line) */
77
+ // this is a single-line comment
78
+ "b": "456", // notice the trailing comma
79
+ }
80
+ """
81
+
82
+ data = jsonxox.loads(sample)
83
+
84
+ assert isinstance(data, dict)
85
+
86
+ assert "a" in data
87
+ assert data["a"] == 123
88
+
89
+ assert "b" in data
90
+ assert data["b"] == "456"
91
+ ```
92
+
93
+ <a id="methods"></a>
94
+ ### Methods
95
+
96
+ The `jsonxox` library provides the following methods, mirroring the standard `json`
97
+ library in its available methods and overall functionality:
98
+
99
+ * `load(handle, **kwargs)` (`object`) – The `load()` method takes a file handle or file
100
+ handle like object that is an instance of `io.TextIOWrapper` or one of its subclasses
101
+ as input and removes any single and multi-line comments as well as any trailing commas
102
+ from the input string contents read from the file handle, and then passes the cleaned
103
+ string to the standard library's `json.loads()` method for quick deserialization to its
104
+ corresponding Python data types. Any additional keyword arguments are passed to the
105
+ `json.loads()` method, as such any additional keyword arguments must be compatible with
106
+ the `json.loads()` method.
107
+
108
+ * `loads(handle, **kwargs)` (`object`) – The `loads()` method takes a string as input
109
+ and removes any single and multi-line comments as well as any trailing commas from the
110
+ input string, and then passes the cleaned string to the standard library's `json.loads()`
111
+ method for quick deserialization to its corresponding Python data types. Any additional
112
+ keyword arguments are passed to the `json.loads()` method, as such any additional
113
+ keyword arguments must be compatible with the `json.loads()` method
114
+
115
+ * `dump(*args, **kwargs)` – The `dump()` method is a passthrough to the standard
116
+ `json` library's `dump()` method and accepts the same arguments and has the same return
117
+ values. Note that the `dump()` method cannot be used to re-save any comments or trailing
118
+ commas that may have been present in the original extended JSON file or string.
119
+
120
+ * `dumps(*args, **kwargs)` (`str`) – The `dumps()` method is a passthrough to the standard
121
+ `json` library's `dumps()` method and accepts the same arguments and has the same return
122
+ values. Note that the `dumps()` method cannot be used to re-save any comments or trailing
123
+ commas that may have been present in the original extended JSON file or string.
124
+
125
+ ### Classes
126
+
127
+ The `jsonxox` library provides the following classes, mirroring the standard `json`
128
+ library in its available classes:
129
+
130
+ * `JSONDecoder` – The `JSONDecoder` class is a direct import of and reference to the
131
+ standard `json` library's `JSONDecoder` class.
132
+
133
+ * `JSONEncoder` – The `JSONEncoder` class is a direct import of and reference to the
134
+ standard `json` library's `JSONEncoder` class.
135
+
136
+ ### Exceptions
137
+
138
+ The `jsonxox` library provides the following exception classes, mirroring the standard
139
+ `json` library in its available exception classes:
140
+
141
+ * `JSONDecodeError` – The `JSONDecodeError` exception class is a direct import of and
142
+ reference to the standard `json` library's `JSONDecodeError` exception class.
143
+
144
+ * `JSONXOXDecodeError` – The `JSONXOXDecodeError` exception class is a subclass of the
145
+ standard `json` library's `JSONDecodeError` class and will be raised by the `jsonxox`
146
+ library if an exception occurs while attempting to clean or load the JSON input.
147
+
148
+ ### Unit Tests
149
+
150
+ The JSONXOX library includes a suite of comprehensive unit tests which ensure that the
151
+ library functionality operates as expected. The unit tests were developed with and are
152
+ run via `pytest`.
153
+
154
+ To ensure that the unit tests are run within a predictable runtime environment where all
155
+ of the necessary dependencies are available, a [Docker](https://www.docker.com) image is
156
+ created within which the tests are run. To run the unit tests, ensure Docker and Docker
157
+ Compose is [installed](https://docs.docker.com/engine/install/), and perform the
158
+ following commands, which will build the Docker image via `docker compose build` and
159
+ then run the tests via `docker compose run` – the output the tests will be displayed:
160
+
161
+ ```shell
162
+ $ docker compose build
163
+ $ docker compose run tests
164
+ ```
165
+
166
+ To run the unit tests with optional command line arguments being passed to `pytest`,
167
+ append the relevant arguments to the `docker compose run tests` command, as follows, for
168
+ example passing `-v` to enable verbose output and `-s` to print standard output:
169
+
170
+ ```shell
171
+ $ docker compose run tests -v -s
172
+ ```
173
+
174
+ See the documentation for [PyTest](https://docs.pytest.org/en/latest/) regarding
175
+ available optional command line arguments.
176
+
177
+ ### Copyright & License Information
178
+
179
+ Copyright © 2023–2026 Daniel Sissman; licensed under the MIT License.
@@ -0,0 +1,15 @@
1
+ LICENSE.md
2
+ README.md
3
+ pyproject.toml
4
+ requirements.development.txt
5
+ requirements.distribution.txt
6
+ requirements.txt
7
+ source/jsonxox/__init__.py
8
+ source/jsonxox/version.txt
9
+ source/jsonxox.egg-info/PKG-INFO
10
+ source/jsonxox.egg-info/SOURCES.txt
11
+ source/jsonxox.egg-info/dependency_links.txt
12
+ source/jsonxox.egg-info/requires.txt
13
+ source/jsonxox.egg-info/top_level.txt
14
+ source/jsonxox.egg-info/zip-safe
15
+ tests/test_jsonxox.py
@@ -0,0 +1,11 @@
1
+
2
+ [development]
3
+ black==26.1.*
4
+ pytest==9.0.*
5
+ pytest-codeblocks==0.17.*
6
+ pyflakes==3.4.*
7
+
8
+ [distribution]
9
+ build
10
+ twine
11
+ wheel
@@ -0,0 +1 @@
1
+ jsonxox
@@ -0,0 +1,281 @@
1
+ import jsonxox
2
+ import io
3
+
4
+
5
+ def test_jsonxox_loads_with_trailing_commas_indented(read: callable):
6
+ """Text the jsonxox.loads() method."""
7
+
8
+ contents: str = read("examples/trailing-commas-indented.jsonx")
9
+
10
+ assert isinstance(contents, str)
11
+
12
+ data = jsonxox.loads(contents)
13
+
14
+ assert isinstance(data, dict)
15
+
16
+ assert "a" in data
17
+ assert data["a"] == 123
18
+
19
+ assert "b" in data
20
+ assert data["b"] == "456"
21
+
22
+ assert "c" in data
23
+ assert data["c"] == 789.10
24
+
25
+
26
+ def test_jsonxox_loads_with_trailing_commas_compacted(read: callable):
27
+ """Text the jsonxox.loads() method."""
28
+
29
+ contents: str = read("examples/trailing-commas-compacted.jsonx")
30
+
31
+ assert isinstance(contents, str)
32
+
33
+ data = jsonxox.loads(contents)
34
+
35
+ assert isinstance(data, dict)
36
+
37
+ assert "a" in data
38
+ assert data["a"] == 123
39
+
40
+ assert "b" in data
41
+ assert data["b"] == "456"
42
+
43
+ assert "c" in data
44
+ assert data["c"] == 789.10
45
+
46
+
47
+ def test_jsonxox_loads_with_single_line_comments_indented(read: callable):
48
+ """Text the jsonxox.loads() method."""
49
+
50
+ contents: str = read("examples/single-line-comments-indented.jsonx")
51
+
52
+ assert isinstance(contents, str)
53
+
54
+ data = jsonxox.loads(contents)
55
+
56
+ assert isinstance(data, dict)
57
+
58
+ assert "a" in data
59
+ assert data["a"] == 123
60
+
61
+ assert "b" in data
62
+ assert data["b"] == "456"
63
+
64
+ assert "c" in data
65
+ assert data["c"] == 789.10
66
+
67
+
68
+ def test_jsonxox_loads_with_single_line_comments_compacted(read: callable):
69
+ """Text the jsonxox.loads() method."""
70
+
71
+ contents: str = read("examples/single-line-comments-compacted.jsonx")
72
+
73
+ assert isinstance(contents, str)
74
+
75
+ data = jsonxox.loads(contents)
76
+
77
+ assert isinstance(data, dict)
78
+
79
+ assert "a" in data
80
+ assert data["a"] == 123
81
+
82
+ assert "b" in data
83
+ assert data["b"] == "456"
84
+
85
+ assert "c" in data
86
+ assert data["c"] == 789.10
87
+
88
+
89
+ def test_jsonxox_loads_with_multi_line_comments_indented(read: callable):
90
+ """Text the jsonxox.loads() method."""
91
+
92
+ contents: str = read("examples/multi-line-comments-indented.jsonx")
93
+
94
+ assert isinstance(contents, str)
95
+
96
+ data = jsonxox.loads(contents)
97
+
98
+ assert isinstance(data, dict)
99
+
100
+ assert "a" in data
101
+ assert data["a"] == 123
102
+
103
+ assert "b" in data
104
+ assert data["b"] == "456"
105
+
106
+ assert "c" in data
107
+ assert data["c"] == 789.10
108
+
109
+
110
+ def test_jsonxox_loads_with_multi_line_comments_compacted(read: callable):
111
+ """Text the jsonxox.loads() method."""
112
+
113
+ contents: str = read("examples/multi-line-comments-compacted.jsonx")
114
+
115
+ assert isinstance(contents, str)
116
+
117
+ data = jsonxox.loads(contents)
118
+
119
+ assert isinstance(data, dict)
120
+
121
+ assert "a" in data
122
+ assert data["a"] == 123
123
+
124
+ assert "b" in data
125
+ assert data["b"] == "456"
126
+
127
+ assert "c" in data
128
+ assert data["c"] == 789.10
129
+
130
+
131
+ def test_jsonxox_load_with_all_combined_indented(path: callable):
132
+ """Text the jsonxox.load() method."""
133
+
134
+ filepath: str = path("examples/all-combined-indented.jsonx")
135
+
136
+ assert isinstance(filepath, str)
137
+
138
+ with open(filepath, "r") as handle:
139
+ data = jsonxox.load(handle)
140
+
141
+ assert isinstance(data, dict)
142
+
143
+ assert "a" in data
144
+ assert data["a"] == 123
145
+
146
+ assert "b" in data
147
+ assert data["b"] == "456"
148
+
149
+ assert "c" in data
150
+ assert data["c"] == 789.10
151
+
152
+
153
+ def test_jsonxox_load_with_all_combined_compacted(path: callable):
154
+ """Text the jsonxox.load() method."""
155
+
156
+ filepath: str = path("examples/all-combined-compacted.jsonx")
157
+
158
+ assert isinstance(filepath, str)
159
+
160
+ with open(filepath, "r") as handle:
161
+ data = jsonxox.load(handle)
162
+
163
+ assert isinstance(data, dict)
164
+
165
+ assert "a" in data
166
+ assert data["a"] == 123
167
+
168
+ assert "b" in data
169
+ assert data["b"] == "456"
170
+
171
+ assert "c" in data
172
+ assert data["c"] == 789.10
173
+
174
+
175
+ def test_jsonxox_loads_with_all_combined_indented(read: callable):
176
+ """Text the jsonxox.loads() method."""
177
+
178
+ contents: str = read("examples/all-combined-indented.jsonx")
179
+
180
+ assert isinstance(contents, str)
181
+
182
+ data = jsonxox.loads(contents)
183
+
184
+ assert isinstance(data, dict)
185
+
186
+ assert "a" in data
187
+ assert data["a"] == 123
188
+
189
+ assert "b" in data
190
+ assert data["b"] == "456"
191
+
192
+ assert "c" in data
193
+ assert data["c"] == 789.10
194
+
195
+
196
+ def test_jsonxox_loads_with_all_combined_compacted(read: callable):
197
+ """Text the jsonxox.loads() method."""
198
+
199
+ contents: str = read("examples/all-combined-compacted.jsonx")
200
+
201
+ assert isinstance(contents, str)
202
+
203
+ data = jsonxox.loads(contents)
204
+
205
+ assert isinstance(data, dict)
206
+
207
+ assert "a" in data
208
+ assert data["a"] == 123
209
+
210
+ assert "b" in data
211
+ assert data["b"] == "456"
212
+
213
+ assert "c" in data
214
+ assert data["c"] == 789.10
215
+
216
+
217
+ def test_jsonxox_dump(path: callable, read: callable):
218
+ """Text the jsonxox.dump() method."""
219
+
220
+ data = {
221
+ "a": 123,
222
+ "b": "456",
223
+ "c": 789.10,
224
+ }
225
+
226
+ assert isinstance(data, dict)
227
+
228
+ assert "a" in data
229
+ assert data["a"] == 123
230
+
231
+ assert "b" in data
232
+ assert data["b"] == "456"
233
+
234
+ assert "c" in data
235
+ assert data["c"] == 789.10
236
+
237
+ with io.StringIO() as handle:
238
+ jsonxox.dump(data, handle, indent=2, ensure_ascii=False)
239
+
240
+ handle.seek(0)
241
+
242
+ dumped: str = handle.read()
243
+
244
+ assert isinstance(dumped, str)
245
+
246
+ contents: str = read("examples/dumped.json")
247
+
248
+ assert isinstance(contents, str)
249
+
250
+ assert dumped == contents
251
+
252
+
253
+ def test_jsonxox_dumps(path: callable, read: callable):
254
+ """Text the jsonxox.dumps() method."""
255
+
256
+ data = {
257
+ "a": 123,
258
+ "b": "456",
259
+ "c": 789.10,
260
+ }
261
+
262
+ assert isinstance(data, dict)
263
+
264
+ assert "a" in data
265
+ assert data["a"] == 123
266
+
267
+ assert "b" in data
268
+ assert data["b"] == "456"
269
+
270
+ assert "c" in data
271
+ assert data["c"] == 789.10
272
+
273
+ dumped: str = jsonxox.dumps(data, indent=2, ensure_ascii=False)
274
+
275
+ assert isinstance(dumped, str)
276
+
277
+ contents: str = read("examples/dumped.json")
278
+
279
+ assert isinstance(contents, str)
280
+
281
+ assert dumped == contents