json2xml 6.0.7__tar.gz → 6.1.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.
- {json2xml-6.0.7 → json2xml-6.1.0}/CONTRIBUTING.rst +12 -1
- {json2xml-6.0.7 → json2xml-6.1.0}/HISTORY.rst +7 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/PKG-INFO +133 -3
- {json2xml-6.0.7 → json2xml-6.1.0}/README.rst +132 -2
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/usage.rst +46 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/__init__.py +1 -1
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/cli.py +25 -5
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/PKG-INFO +133 -3
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/SOURCES.txt +1 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/pyproject.toml +1 -1
- {json2xml-6.0.7 → json2xml-6.1.0}/setup.cfg +1 -1
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_cli.py +35 -5
- json2xml-6.1.0/tests/test_dicttoxml_fast_fallback.py +98 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_utils.py +2 -2
- {json2xml-6.0.7 → json2xml-6.1.0}/AUTHORS.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/LICENSE +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/MANIFEST.in +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/Makefile +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/authors.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/benchmarks.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/conf.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/contributing.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/history.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/index.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/installation.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/json2xml.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/make.bat +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/modules.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/docs/readme.rst +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/dicttoxml.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/dicttoxml_fast.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/json2xml.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/types.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml/utils.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/dependency_links.txt +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/entry_points.txt +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/requires.txt +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/json2xml.egg-info/top_level.txt +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/requirements.in +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/setup.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/__init__.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/conftest.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_dict2xml.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_json2xml.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_missing_coverage.py +0 -0
- {json2xml-6.0.7 → json2xml-6.1.0}/tests/test_rust_dicttoxml.py +0 -0
|
@@ -9,6 +9,17 @@ helps, and credit will always be given.
|
|
|
9
9
|
|
|
10
10
|
You can contribute in many ways:
|
|
11
11
|
|
|
12
|
+
Good First Contributions
|
|
13
|
+
------------------------
|
|
14
|
+
|
|
15
|
+
If you are new to the project, these are useful places to start:
|
|
16
|
+
|
|
17
|
+
* Improve examples for real-world API responses, files, and command-line usage.
|
|
18
|
+
* Add small tests around conversion options such as wrappers, list handling, CDATA, and XPath output.
|
|
19
|
+
* Improve benchmark documentation or add a reproducible benchmark case.
|
|
20
|
+
* Polish CLI behavior, error messages, and help text.
|
|
21
|
+
* Review the roadmap in ``ROADMAP.md`` and open a focused issue before starting larger changes.
|
|
22
|
+
|
|
12
23
|
Types of Contributions
|
|
13
24
|
----------------------
|
|
14
25
|
|
|
@@ -99,7 +110,7 @@ Ready to contribute? Here's how to set up `json2xml` for local development.
|
|
|
99
110
|
Rust Extension Development
|
|
100
111
|
--------------------------
|
|
101
112
|
|
|
102
|
-
The ``json2xml-rs`` Rust extension provides ~
|
|
113
|
+
The ``json2xml-rs`` Rust extension provides ~57-129x faster performance in the current benchmark. If you want to contribute to the Rust extension:
|
|
103
114
|
|
|
104
115
|
**Prerequisites**
|
|
105
116
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: json2xml
|
|
3
|
-
Version: 6.0
|
|
3
|
+
Version: 6.1.0
|
|
4
4
|
Summary: Simple Python Library to convert JSON to XML
|
|
5
5
|
Author-email: Vinit Kumar <mail@vinitkumar.me>
|
|
6
6
|
License: Apache Software License 2.0
|
|
@@ -54,7 +54,7 @@ json2xml
|
|
|
54
54
|
.. image:: https://codecov.io/gh/vinitkumar/json2xml/branch/master/graph/badge.svg?token=Yt2h55eTL2
|
|
55
55
|
:target: https://codecov.io/gh/vinitkumar/json2xml
|
|
56
56
|
|
|
57
|
-
json2xml is a Python library
|
|
57
|
+
json2xml is a Python library and CLI for converting JSON data into XML. It is designed for teams that need predictable XML output, Python-first ergonomics, and a faster native path when conversion speed matters.
|
|
58
58
|
|
|
59
59
|
Documentation: https://json2xml.readthedocs.io.
|
|
60
60
|
|
|
@@ -62,6 +62,137 @@ The library was initially dependent on the `dict2xml` project, but it has now be
|
|
|
62
62
|
|
|
63
63
|
**Looking for a Go version?** Check out `json2xml-go <https://github.com/vinitkumar/json2xml-go>`_, a Go port of this library with identical features and a native CLI tool.
|
|
64
64
|
|
|
65
|
+
Why json2xml?
|
|
66
|
+
^^^^^^^^^^^^^
|
|
67
|
+
|
|
68
|
+
Use json2xml when you need:
|
|
69
|
+
|
|
70
|
+
* A Python library for turning dictionaries, strings, files, or API responses into XML
|
|
71
|
+
* A command-line tool for quick JSON-to-XML conversion from scripts and terminals
|
|
72
|
+
* Optional Rust acceleration with automatic fallback to the pure Python implementation
|
|
73
|
+
* XPath 3.1 compatible output when standards-friendly XML is required
|
|
74
|
+
* A small, focused project with tests, docs, benchmarks, and multiple native implementation experiments
|
|
75
|
+
|
|
76
|
+
Quick Start
|
|
77
|
+
^^^^^^^^^^^
|
|
78
|
+
|
|
79
|
+
Install the Python package:
|
|
80
|
+
|
|
81
|
+
.. code-block:: console
|
|
82
|
+
|
|
83
|
+
pip install json2xml
|
|
84
|
+
|
|
85
|
+
Use it from Python:
|
|
86
|
+
|
|
87
|
+
.. code-block:: python
|
|
88
|
+
|
|
89
|
+
from json2xml import json2xml
|
|
90
|
+
from json2xml.utils import readfromstring
|
|
91
|
+
|
|
92
|
+
data = readfromstring('{"name": "Ada", "language": "Python"}')
|
|
93
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
94
|
+
|
|
95
|
+
Use it from the terminal:
|
|
96
|
+
|
|
97
|
+
.. code-block:: console
|
|
98
|
+
|
|
99
|
+
json2xml-py -s '{"name": "Ada", "language": "Python"}'
|
|
100
|
+
|
|
101
|
+
Install the accelerated path:
|
|
102
|
+
|
|
103
|
+
.. code-block:: console
|
|
104
|
+
|
|
105
|
+
pip install json2xml[fast]
|
|
106
|
+
|
|
107
|
+
Real-world Examples
|
|
108
|
+
^^^^^^^^^^^^^^^^^^^
|
|
109
|
+
|
|
110
|
+
Convert a JSON API response in Python when another system expects XML:
|
|
111
|
+
|
|
112
|
+
.. code-block:: python
|
|
113
|
+
|
|
114
|
+
from json2xml import json2xml
|
|
115
|
+
|
|
116
|
+
api_response = {"user": {"id": 7, "name": "Ada"}, "active": True}
|
|
117
|
+
print(json2xml.Json2xml(api_response, pretty=False).to_xml().decode("utf-8"))
|
|
118
|
+
|
|
119
|
+
Output:
|
|
120
|
+
|
|
121
|
+
.. code-block:: xml
|
|
122
|
+
|
|
123
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><user type="dict"><id type="int">7</id><name type="str">Ada</name></user><active type="bool">true</active></all>
|
|
124
|
+
|
|
125
|
+
Convert a local JSON export from the shell:
|
|
126
|
+
|
|
127
|
+
.. code-block:: console
|
|
128
|
+
|
|
129
|
+
cat > orders.json <<'JSON'
|
|
130
|
+
{"orders":[{"id":"A100","total":19.99},{"id":"A101","total":5.5}]}
|
|
131
|
+
JSON
|
|
132
|
+
json2xml-py --no-pretty --no-type orders.json
|
|
133
|
+
|
|
134
|
+
Output:
|
|
135
|
+
|
|
136
|
+
.. code-block:: xml
|
|
137
|
+
|
|
138
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><orders><item><id>A100</id><total>19.99</total></item><item><id>A101</id><total>5.5</total></item></orders></all>
|
|
139
|
+
|
|
140
|
+
Convert stdin in a shell pipeline:
|
|
141
|
+
|
|
142
|
+
.. code-block:: console
|
|
143
|
+
|
|
144
|
+
printf '%s\n' '{"event":"deploy","status":"ok"}' | json2xml-py --no-pretty --no-type -
|
|
145
|
+
|
|
146
|
+
Output:
|
|
147
|
+
|
|
148
|
+
.. code-block:: xml
|
|
149
|
+
|
|
150
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><event>deploy</event><status>ok</status></all>
|
|
151
|
+
|
|
152
|
+
Performance Snapshot
|
|
153
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
154
|
+
|
|
155
|
+
The optional Rust extension is the fastest path for Python callers because it avoids process startup overhead and falls back to pure Python when a feature is not supported natively.
|
|
156
|
+
|
|
157
|
+
.. list-table::
|
|
158
|
+
:header-rows: 1
|
|
159
|
+
:widths: 30 20 20 15
|
|
160
|
+
|
|
161
|
+
* - Test Case
|
|
162
|
+
- Pure Python
|
|
163
|
+
- Rust Extension
|
|
164
|
+
- Speedup
|
|
165
|
+
* - **Small JSON** (47 bytes)
|
|
166
|
+
- 31.49µs
|
|
167
|
+
- 0.55µs
|
|
168
|
+
- **56.8x**
|
|
169
|
+
* - **Medium JSON** (3.2 KB)
|
|
170
|
+
- 1.69ms
|
|
171
|
+
- 16.15µs
|
|
172
|
+
- **105.0x**
|
|
173
|
+
* - **Large JSON** (32 KB)
|
|
174
|
+
- 17.97ms
|
|
175
|
+
- 168.21µs
|
|
176
|
+
- **106.8x**
|
|
177
|
+
* - **Very Large JSON** (323 KB)
|
|
178
|
+
- 183.33ms
|
|
179
|
+
- 1.42ms
|
|
180
|
+
- **129.0x**
|
|
181
|
+
|
|
182
|
+
See `BENCHMARKS.md <https://github.com/vinitkumar/json2xml/blob/master/BENCHMARKS.md>`_ for the full Python, Rust, Go, and Zig comparison.
|
|
183
|
+
|
|
184
|
+
Project Roadmap
|
|
185
|
+
^^^^^^^^^^^^^^^
|
|
186
|
+
|
|
187
|
+
The next phase of json2xml is focused on making the project easier to adopt, benchmark, and contribute to:
|
|
188
|
+
|
|
189
|
+
* Improve examples for common API, file, and CLI workflows
|
|
190
|
+
* Add clearer contribution paths for docs, CLI polish, and benchmark coverage
|
|
191
|
+
* Keep comparing Python, Rust, Go, and Zig implementations with reproducible benchmarks
|
|
192
|
+
* Continue hardening the Rust extension fallback behavior across platforms
|
|
193
|
+
|
|
194
|
+
See `ROADMAP.md <https://github.com/vinitkumar/json2xml/blob/master/ROADMAP.md>`_ for more detail.
|
|
195
|
+
|
|
65
196
|
|
|
66
197
|
|
|
67
198
|
Architecture Diagram
|
|
@@ -574,4 +705,3 @@ Help and Support to maintain this project
|
|
|
574
705
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
575
706
|
|
|
576
707
|
- You can sponsor my work for this plugin here: https://github.com/sponsors/vinitkumar/
|
|
577
|
-
|
|
@@ -18,7 +18,7 @@ json2xml
|
|
|
18
18
|
.. image:: https://codecov.io/gh/vinitkumar/json2xml/branch/master/graph/badge.svg?token=Yt2h55eTL2
|
|
19
19
|
:target: https://codecov.io/gh/vinitkumar/json2xml
|
|
20
20
|
|
|
21
|
-
json2xml is a Python library
|
|
21
|
+
json2xml is a Python library and CLI for converting JSON data into XML. It is designed for teams that need predictable XML output, Python-first ergonomics, and a faster native path when conversion speed matters.
|
|
22
22
|
|
|
23
23
|
Documentation: https://json2xml.readthedocs.io.
|
|
24
24
|
|
|
@@ -26,6 +26,137 @@ The library was initially dependent on the `dict2xml` project, but it has now be
|
|
|
26
26
|
|
|
27
27
|
**Looking for a Go version?** Check out `json2xml-go <https://github.com/vinitkumar/json2xml-go>`_, a Go port of this library with identical features and a native CLI tool.
|
|
28
28
|
|
|
29
|
+
Why json2xml?
|
|
30
|
+
^^^^^^^^^^^^^
|
|
31
|
+
|
|
32
|
+
Use json2xml when you need:
|
|
33
|
+
|
|
34
|
+
* A Python library for turning dictionaries, strings, files, or API responses into XML
|
|
35
|
+
* A command-line tool for quick JSON-to-XML conversion from scripts and terminals
|
|
36
|
+
* Optional Rust acceleration with automatic fallback to the pure Python implementation
|
|
37
|
+
* XPath 3.1 compatible output when standards-friendly XML is required
|
|
38
|
+
* A small, focused project with tests, docs, benchmarks, and multiple native implementation experiments
|
|
39
|
+
|
|
40
|
+
Quick Start
|
|
41
|
+
^^^^^^^^^^^
|
|
42
|
+
|
|
43
|
+
Install the Python package:
|
|
44
|
+
|
|
45
|
+
.. code-block:: console
|
|
46
|
+
|
|
47
|
+
pip install json2xml
|
|
48
|
+
|
|
49
|
+
Use it from Python:
|
|
50
|
+
|
|
51
|
+
.. code-block:: python
|
|
52
|
+
|
|
53
|
+
from json2xml import json2xml
|
|
54
|
+
from json2xml.utils import readfromstring
|
|
55
|
+
|
|
56
|
+
data = readfromstring('{"name": "Ada", "language": "Python"}')
|
|
57
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
58
|
+
|
|
59
|
+
Use it from the terminal:
|
|
60
|
+
|
|
61
|
+
.. code-block:: console
|
|
62
|
+
|
|
63
|
+
json2xml-py -s '{"name": "Ada", "language": "Python"}'
|
|
64
|
+
|
|
65
|
+
Install the accelerated path:
|
|
66
|
+
|
|
67
|
+
.. code-block:: console
|
|
68
|
+
|
|
69
|
+
pip install json2xml[fast]
|
|
70
|
+
|
|
71
|
+
Real-world Examples
|
|
72
|
+
^^^^^^^^^^^^^^^^^^^
|
|
73
|
+
|
|
74
|
+
Convert a JSON API response in Python when another system expects XML:
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
from json2xml import json2xml
|
|
79
|
+
|
|
80
|
+
api_response = {"user": {"id": 7, "name": "Ada"}, "active": True}
|
|
81
|
+
print(json2xml.Json2xml(api_response, pretty=False).to_xml().decode("utf-8"))
|
|
82
|
+
|
|
83
|
+
Output:
|
|
84
|
+
|
|
85
|
+
.. code-block:: xml
|
|
86
|
+
|
|
87
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><user type="dict"><id type="int">7</id><name type="str">Ada</name></user><active type="bool">true</active></all>
|
|
88
|
+
|
|
89
|
+
Convert a local JSON export from the shell:
|
|
90
|
+
|
|
91
|
+
.. code-block:: console
|
|
92
|
+
|
|
93
|
+
cat > orders.json <<'JSON'
|
|
94
|
+
{"orders":[{"id":"A100","total":19.99},{"id":"A101","total":5.5}]}
|
|
95
|
+
JSON
|
|
96
|
+
json2xml-py --no-pretty --no-type orders.json
|
|
97
|
+
|
|
98
|
+
Output:
|
|
99
|
+
|
|
100
|
+
.. code-block:: xml
|
|
101
|
+
|
|
102
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><orders><item><id>A100</id><total>19.99</total></item><item><id>A101</id><total>5.5</total></item></orders></all>
|
|
103
|
+
|
|
104
|
+
Convert stdin in a shell pipeline:
|
|
105
|
+
|
|
106
|
+
.. code-block:: console
|
|
107
|
+
|
|
108
|
+
printf '%s\n' '{"event":"deploy","status":"ok"}' | json2xml-py --no-pretty --no-type -
|
|
109
|
+
|
|
110
|
+
Output:
|
|
111
|
+
|
|
112
|
+
.. code-block:: xml
|
|
113
|
+
|
|
114
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><event>deploy</event><status>ok</status></all>
|
|
115
|
+
|
|
116
|
+
Performance Snapshot
|
|
117
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
118
|
+
|
|
119
|
+
The optional Rust extension is the fastest path for Python callers because it avoids process startup overhead and falls back to pure Python when a feature is not supported natively.
|
|
120
|
+
|
|
121
|
+
.. list-table::
|
|
122
|
+
:header-rows: 1
|
|
123
|
+
:widths: 30 20 20 15
|
|
124
|
+
|
|
125
|
+
* - Test Case
|
|
126
|
+
- Pure Python
|
|
127
|
+
- Rust Extension
|
|
128
|
+
- Speedup
|
|
129
|
+
* - **Small JSON** (47 bytes)
|
|
130
|
+
- 31.49µs
|
|
131
|
+
- 0.55µs
|
|
132
|
+
- **56.8x**
|
|
133
|
+
* - **Medium JSON** (3.2 KB)
|
|
134
|
+
- 1.69ms
|
|
135
|
+
- 16.15µs
|
|
136
|
+
- **105.0x**
|
|
137
|
+
* - **Large JSON** (32 KB)
|
|
138
|
+
- 17.97ms
|
|
139
|
+
- 168.21µs
|
|
140
|
+
- **106.8x**
|
|
141
|
+
* - **Very Large JSON** (323 KB)
|
|
142
|
+
- 183.33ms
|
|
143
|
+
- 1.42ms
|
|
144
|
+
- **129.0x**
|
|
145
|
+
|
|
146
|
+
See `BENCHMARKS.md <https://github.com/vinitkumar/json2xml/blob/master/BENCHMARKS.md>`_ for the full Python, Rust, Go, and Zig comparison.
|
|
147
|
+
|
|
148
|
+
Project Roadmap
|
|
149
|
+
^^^^^^^^^^^^^^^
|
|
150
|
+
|
|
151
|
+
The next phase of json2xml is focused on making the project easier to adopt, benchmark, and contribute to:
|
|
152
|
+
|
|
153
|
+
* Improve examples for common API, file, and CLI workflows
|
|
154
|
+
* Add clearer contribution paths for docs, CLI polish, and benchmark coverage
|
|
155
|
+
* Keep comparing Python, Rust, Go, and Zig implementations with reproducible benchmarks
|
|
156
|
+
* Continue hardening the Rust extension fallback behavior across platforms
|
|
157
|
+
|
|
158
|
+
See `ROADMAP.md <https://github.com/vinitkumar/json2xml/blob/master/ROADMAP.md>`_ for more detail.
|
|
159
|
+
|
|
29
160
|
|
|
30
161
|
|
|
31
162
|
Architecture Diagram
|
|
@@ -538,4 +669,3 @@ Help and Support to maintain this project
|
|
|
538
669
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
539
670
|
|
|
540
671
|
- You can sponsor my work for this plugin here: https://github.com/sponsors/vinitkumar/
|
|
541
|
-
|
|
@@ -33,6 +33,52 @@ Here's how to use each method:
|
|
|
33
33
|
print(json2xml.Json2xml(data).to_xml())
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
Real-world Examples
|
|
37
|
+
-------------------
|
|
38
|
+
|
|
39
|
+
Convert a JSON API response in Python when another service expects XML:
|
|
40
|
+
|
|
41
|
+
.. code-block:: python
|
|
42
|
+
|
|
43
|
+
from json2xml import json2xml
|
|
44
|
+
|
|
45
|
+
api_response = {"user": {"id": 7, "name": "Ada"}, "active": True}
|
|
46
|
+
print(json2xml.Json2xml(api_response, pretty=False).to_xml().decode("utf-8"))
|
|
47
|
+
|
|
48
|
+
Output:
|
|
49
|
+
|
|
50
|
+
.. code-block:: xml
|
|
51
|
+
|
|
52
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><user type="dict"><id type="int">7</id><name type="str">Ada</name></user><active type="bool">true</active></all>
|
|
53
|
+
|
|
54
|
+
Convert a local JSON export from the shell:
|
|
55
|
+
|
|
56
|
+
.. code-block:: console
|
|
57
|
+
|
|
58
|
+
cat > orders.json <<'JSON'
|
|
59
|
+
{"orders":[{"id":"A100","total":19.99},{"id":"A101","total":5.5}]}
|
|
60
|
+
JSON
|
|
61
|
+
json2xml-py --no-pretty --no-type orders.json
|
|
62
|
+
|
|
63
|
+
Output:
|
|
64
|
+
|
|
65
|
+
.. code-block:: xml
|
|
66
|
+
|
|
67
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><orders><item><id>A100</id><total>19.99</total></item><item><id>A101</id><total>5.5</total></item></orders></all>
|
|
68
|
+
|
|
69
|
+
Convert stdin in a shell pipeline:
|
|
70
|
+
|
|
71
|
+
.. code-block:: console
|
|
72
|
+
|
|
73
|
+
printf '%s\n' '{"event":"deploy","status":"ok"}' | json2xml-py --no-pretty --no-type -
|
|
74
|
+
|
|
75
|
+
Output:
|
|
76
|
+
|
|
77
|
+
.. code-block:: xml
|
|
78
|
+
|
|
79
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><event>deploy</event><status>ok</status></all>
|
|
80
|
+
|
|
81
|
+
|
|
36
82
|
Constructor Parameters
|
|
37
83
|
----------------------
|
|
38
84
|
|
|
@@ -43,6 +43,7 @@ from __future__ import annotations
|
|
|
43
43
|
|
|
44
44
|
import argparse
|
|
45
45
|
import sys
|
|
46
|
+
from pathlib import Path
|
|
46
47
|
from typing import NoReturn
|
|
47
48
|
|
|
48
49
|
from json2xml import __version__
|
|
@@ -263,22 +264,36 @@ def read_input(args: argparse.Namespace) -> JSONValue:
|
|
|
263
264
|
try:
|
|
264
265
|
return readfromstring(args.string)
|
|
265
266
|
except StringReadError as e:
|
|
266
|
-
exit_with_error(
|
|
267
|
+
exit_with_error(
|
|
268
|
+
"Error: Invalid JSON in --string input. "
|
|
269
|
+
f"Pass a valid JSON object, array, string, number, boolean, or null. ({e})"
|
|
270
|
+
)
|
|
267
271
|
|
|
268
272
|
if args.input_file:
|
|
269
273
|
if args.input_file == "-":
|
|
270
274
|
# Read from stdin
|
|
271
275
|
return read_from_stdin()
|
|
276
|
+
if not Path(args.input_file).is_file():
|
|
277
|
+
exit_with_error(
|
|
278
|
+
f"Error: JSON file not found: {args.input_file}. "
|
|
279
|
+
"Check the path or use - to read JSON from stdin."
|
|
280
|
+
)
|
|
272
281
|
try:
|
|
273
282
|
return readfromjson(args.input_file)
|
|
274
283
|
except JSONReadError as e:
|
|
275
|
-
exit_with_error(
|
|
284
|
+
exit_with_error(
|
|
285
|
+
f"Error: Could not parse JSON file: {args.input_file}. "
|
|
286
|
+
f"Check that the file contains valid JSON. ({e})"
|
|
287
|
+
)
|
|
276
288
|
|
|
277
289
|
# Check if there's data on stdin
|
|
278
290
|
if not sys.stdin.isatty():
|
|
279
291
|
return read_from_stdin()
|
|
280
292
|
|
|
281
|
-
exit_with_error(
|
|
293
|
+
exit_with_error(
|
|
294
|
+
"Error: No input provided. Pass a JSON file, use - for stdin, "
|
|
295
|
+
"or provide --string/--url."
|
|
296
|
+
)
|
|
282
297
|
|
|
283
298
|
|
|
284
299
|
def read_from_stdin() -> JSONValue:
|
|
@@ -294,10 +309,15 @@ def read_from_stdin() -> JSONValue:
|
|
|
294
309
|
try:
|
|
295
310
|
json_str = sys.stdin.read().strip()
|
|
296
311
|
if not json_str:
|
|
297
|
-
exit_with_error(
|
|
312
|
+
exit_with_error(
|
|
313
|
+
"Error: Empty stdin. Pipe JSON into stdin or pass a file/--string."
|
|
314
|
+
)
|
|
298
315
|
return readfromstring(json_str)
|
|
299
316
|
except StringReadError as e:
|
|
300
|
-
exit_with_error(
|
|
317
|
+
exit_with_error(
|
|
318
|
+
"Error: Invalid JSON from stdin. Pipe valid JSON into stdin "
|
|
319
|
+
f"or pass a file/--string. ({e})"
|
|
320
|
+
)
|
|
301
321
|
|
|
302
322
|
|
|
303
323
|
def write_output(output: str | bytes, output_file: str | None) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: json2xml
|
|
3
|
-
Version: 6.0
|
|
3
|
+
Version: 6.1.0
|
|
4
4
|
Summary: Simple Python Library to convert JSON to XML
|
|
5
5
|
Author-email: Vinit Kumar <mail@vinitkumar.me>
|
|
6
6
|
License: Apache Software License 2.0
|
|
@@ -54,7 +54,7 @@ json2xml
|
|
|
54
54
|
.. image:: https://codecov.io/gh/vinitkumar/json2xml/branch/master/graph/badge.svg?token=Yt2h55eTL2
|
|
55
55
|
:target: https://codecov.io/gh/vinitkumar/json2xml
|
|
56
56
|
|
|
57
|
-
json2xml is a Python library
|
|
57
|
+
json2xml is a Python library and CLI for converting JSON data into XML. It is designed for teams that need predictable XML output, Python-first ergonomics, and a faster native path when conversion speed matters.
|
|
58
58
|
|
|
59
59
|
Documentation: https://json2xml.readthedocs.io.
|
|
60
60
|
|
|
@@ -62,6 +62,137 @@ The library was initially dependent on the `dict2xml` project, but it has now be
|
|
|
62
62
|
|
|
63
63
|
**Looking for a Go version?** Check out `json2xml-go <https://github.com/vinitkumar/json2xml-go>`_, a Go port of this library with identical features and a native CLI tool.
|
|
64
64
|
|
|
65
|
+
Why json2xml?
|
|
66
|
+
^^^^^^^^^^^^^
|
|
67
|
+
|
|
68
|
+
Use json2xml when you need:
|
|
69
|
+
|
|
70
|
+
* A Python library for turning dictionaries, strings, files, or API responses into XML
|
|
71
|
+
* A command-line tool for quick JSON-to-XML conversion from scripts and terminals
|
|
72
|
+
* Optional Rust acceleration with automatic fallback to the pure Python implementation
|
|
73
|
+
* XPath 3.1 compatible output when standards-friendly XML is required
|
|
74
|
+
* A small, focused project with tests, docs, benchmarks, and multiple native implementation experiments
|
|
75
|
+
|
|
76
|
+
Quick Start
|
|
77
|
+
^^^^^^^^^^^
|
|
78
|
+
|
|
79
|
+
Install the Python package:
|
|
80
|
+
|
|
81
|
+
.. code-block:: console
|
|
82
|
+
|
|
83
|
+
pip install json2xml
|
|
84
|
+
|
|
85
|
+
Use it from Python:
|
|
86
|
+
|
|
87
|
+
.. code-block:: python
|
|
88
|
+
|
|
89
|
+
from json2xml import json2xml
|
|
90
|
+
from json2xml.utils import readfromstring
|
|
91
|
+
|
|
92
|
+
data = readfromstring('{"name": "Ada", "language": "Python"}')
|
|
93
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
94
|
+
|
|
95
|
+
Use it from the terminal:
|
|
96
|
+
|
|
97
|
+
.. code-block:: console
|
|
98
|
+
|
|
99
|
+
json2xml-py -s '{"name": "Ada", "language": "Python"}'
|
|
100
|
+
|
|
101
|
+
Install the accelerated path:
|
|
102
|
+
|
|
103
|
+
.. code-block:: console
|
|
104
|
+
|
|
105
|
+
pip install json2xml[fast]
|
|
106
|
+
|
|
107
|
+
Real-world Examples
|
|
108
|
+
^^^^^^^^^^^^^^^^^^^
|
|
109
|
+
|
|
110
|
+
Convert a JSON API response in Python when another system expects XML:
|
|
111
|
+
|
|
112
|
+
.. code-block:: python
|
|
113
|
+
|
|
114
|
+
from json2xml import json2xml
|
|
115
|
+
|
|
116
|
+
api_response = {"user": {"id": 7, "name": "Ada"}, "active": True}
|
|
117
|
+
print(json2xml.Json2xml(api_response, pretty=False).to_xml().decode("utf-8"))
|
|
118
|
+
|
|
119
|
+
Output:
|
|
120
|
+
|
|
121
|
+
.. code-block:: xml
|
|
122
|
+
|
|
123
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><user type="dict"><id type="int">7</id><name type="str">Ada</name></user><active type="bool">true</active></all>
|
|
124
|
+
|
|
125
|
+
Convert a local JSON export from the shell:
|
|
126
|
+
|
|
127
|
+
.. code-block:: console
|
|
128
|
+
|
|
129
|
+
cat > orders.json <<'JSON'
|
|
130
|
+
{"orders":[{"id":"A100","total":19.99},{"id":"A101","total":5.5}]}
|
|
131
|
+
JSON
|
|
132
|
+
json2xml-py --no-pretty --no-type orders.json
|
|
133
|
+
|
|
134
|
+
Output:
|
|
135
|
+
|
|
136
|
+
.. code-block:: xml
|
|
137
|
+
|
|
138
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><orders><item><id>A100</id><total>19.99</total></item><item><id>A101</id><total>5.5</total></item></orders></all>
|
|
139
|
+
|
|
140
|
+
Convert stdin in a shell pipeline:
|
|
141
|
+
|
|
142
|
+
.. code-block:: console
|
|
143
|
+
|
|
144
|
+
printf '%s\n' '{"event":"deploy","status":"ok"}' | json2xml-py --no-pretty --no-type -
|
|
145
|
+
|
|
146
|
+
Output:
|
|
147
|
+
|
|
148
|
+
.. code-block:: xml
|
|
149
|
+
|
|
150
|
+
<?xml version="1.0" encoding="UTF-8" ?><all><event>deploy</event><status>ok</status></all>
|
|
151
|
+
|
|
152
|
+
Performance Snapshot
|
|
153
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
154
|
+
|
|
155
|
+
The optional Rust extension is the fastest path for Python callers because it avoids process startup overhead and falls back to pure Python when a feature is not supported natively.
|
|
156
|
+
|
|
157
|
+
.. list-table::
|
|
158
|
+
:header-rows: 1
|
|
159
|
+
:widths: 30 20 20 15
|
|
160
|
+
|
|
161
|
+
* - Test Case
|
|
162
|
+
- Pure Python
|
|
163
|
+
- Rust Extension
|
|
164
|
+
- Speedup
|
|
165
|
+
* - **Small JSON** (47 bytes)
|
|
166
|
+
- 31.49µs
|
|
167
|
+
- 0.55µs
|
|
168
|
+
- **56.8x**
|
|
169
|
+
* - **Medium JSON** (3.2 KB)
|
|
170
|
+
- 1.69ms
|
|
171
|
+
- 16.15µs
|
|
172
|
+
- **105.0x**
|
|
173
|
+
* - **Large JSON** (32 KB)
|
|
174
|
+
- 17.97ms
|
|
175
|
+
- 168.21µs
|
|
176
|
+
- **106.8x**
|
|
177
|
+
* - **Very Large JSON** (323 KB)
|
|
178
|
+
- 183.33ms
|
|
179
|
+
- 1.42ms
|
|
180
|
+
- **129.0x**
|
|
181
|
+
|
|
182
|
+
See `BENCHMARKS.md <https://github.com/vinitkumar/json2xml/blob/master/BENCHMARKS.md>`_ for the full Python, Rust, Go, and Zig comparison.
|
|
183
|
+
|
|
184
|
+
Project Roadmap
|
|
185
|
+
^^^^^^^^^^^^^^^
|
|
186
|
+
|
|
187
|
+
The next phase of json2xml is focused on making the project easier to adopt, benchmark, and contribute to:
|
|
188
|
+
|
|
189
|
+
* Improve examples for common API, file, and CLI workflows
|
|
190
|
+
* Add clearer contribution paths for docs, CLI polish, and benchmark coverage
|
|
191
|
+
* Keep comparing Python, Rust, Go, and Zig implementations with reproducible benchmarks
|
|
192
|
+
* Continue hardening the Rust extension fallback behavior across platforms
|
|
193
|
+
|
|
194
|
+
See `ROADMAP.md <https://github.com/vinitkumar/json2xml/blob/master/ROADMAP.md>`_ for more detail.
|
|
195
|
+
|
|
65
196
|
|
|
66
197
|
|
|
67
198
|
Architecture Diagram
|
|
@@ -574,4 +705,3 @@ Help and Support to maintain this project
|
|
|
574
705
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
575
706
|
|
|
576
707
|
- You can sponsor my work for this plugin here: https://github.com/sponsors/vinitkumar/
|
|
577
|
-
|
|
@@ -187,7 +187,7 @@ class TestCLI:
|
|
|
187
187
|
text=True,
|
|
188
188
|
)
|
|
189
189
|
assert result.returncode == 1
|
|
190
|
-
assert "
|
|
190
|
+
assert "Invalid JSON in --string input" in result.stderr
|
|
191
191
|
|
|
192
192
|
def test_no_input_error(self) -> None:
|
|
193
193
|
"""Test error when no input is provided."""
|
|
@@ -201,6 +201,7 @@ class TestCLI:
|
|
|
201
201
|
)
|
|
202
202
|
# Should fail with no input error
|
|
203
203
|
assert result.returncode == 1
|
|
204
|
+
assert "Empty stdin" in result.stderr
|
|
204
205
|
|
|
205
206
|
def test_list_input(self) -> None:
|
|
206
207
|
"""Test handling of JSON array input."""
|
|
@@ -267,7 +268,7 @@ class TestCLI:
|
|
|
267
268
|
text=True,
|
|
268
269
|
)
|
|
269
270
|
assert result.returncode == 1
|
|
270
|
-
assert "Empty
|
|
271
|
+
assert "Empty stdin" in result.stderr
|
|
271
272
|
|
|
272
273
|
def test_stdin_whitespace_only(self) -> None:
|
|
273
274
|
"""Test error handling when stdin contains only whitespace."""
|
|
@@ -303,8 +304,10 @@ class TestCLI:
|
|
|
303
304
|
text=True,
|
|
304
305
|
)
|
|
305
306
|
assert result.returncode == 1
|
|
306
|
-
assert "
|
|
307
|
+
assert "JSON file not found" in result.stderr
|
|
308
|
+
assert "use - to read JSON from stdin" in result.stderr
|
|
307
309
|
|
|
310
|
+
# @lat: [[tests#CLI failure messages#Invalid file JSON names the source]]
|
|
308
311
|
def test_invalid_json_file(self) -> None:
|
|
309
312
|
"""Test error handling for file with invalid JSON content."""
|
|
310
313
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
@@ -318,7 +321,8 @@ class TestCLI:
|
|
|
318
321
|
)
|
|
319
322
|
|
|
320
323
|
assert result.returncode == 1
|
|
321
|
-
assert "
|
|
324
|
+
assert "Could not parse JSON file" in result.stderr
|
|
325
|
+
assert str(json_file) in result.stderr
|
|
322
326
|
|
|
323
327
|
def test_output_file_permission_error(self) -> None:
|
|
324
328
|
"""Test error handling when output file cannot be written."""
|
|
@@ -553,8 +557,34 @@ class TestCLIUnitTests:
|
|
|
553
557
|
assert exc_info.value.code == 1
|
|
554
558
|
|
|
555
559
|
captured = capsys.readouterr()
|
|
556
|
-
assert "
|
|
560
|
+
assert "JSON file not found" in captured.err
|
|
561
|
+
|
|
562
|
+
def test_read_input_existing_json_file_parse_error(self, capsys: CaptureFixture[str]) -> None:
|
|
563
|
+
"""Test read_input keeps parse failures distinct from missing file failures."""
|
|
564
|
+
from json2xml.utils import JSONReadError
|
|
565
|
+
|
|
566
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
567
|
+
json_file = Path(tmpdir) / "invalid.json"
|
|
568
|
+
json_file.write_text("{")
|
|
569
|
+
|
|
570
|
+
with patch("json2xml.cli.readfromjson") as mock_read:
|
|
571
|
+
mock_read.side_effect = JSONReadError("Invalid JSON File")
|
|
572
|
+
|
|
573
|
+
args = MagicMock()
|
|
574
|
+
args.url = None
|
|
575
|
+
args.string = None
|
|
576
|
+
args.input_file = str(json_file)
|
|
577
|
+
|
|
578
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
579
|
+
read_input(args)
|
|
580
|
+
|
|
581
|
+
assert exc_info.value.code == 1
|
|
582
|
+
|
|
583
|
+
captured = capsys.readouterr()
|
|
584
|
+
assert "Could not parse JSON file" in captured.err
|
|
585
|
+
assert str(json_file) in captured.err
|
|
557
586
|
|
|
587
|
+
# @lat: [[tests#CLI failure messages#No input is actionable]]
|
|
558
588
|
def test_read_input_no_input_tty(self, capsys: CaptureFixture[str]) -> None:
|
|
559
589
|
"""Test read_input exits when no input provided and stdin is a tty."""
|
|
560
590
|
with patch("sys.stdin.isatty", return_value=True):
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Tests for optional Rust backend selection in dicttoxml_fast."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
from unittest.mock import Mock
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
import json2xml.dicttoxml_fast as fast_module
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _force_rust_backend(monkeypatch: pytest.MonkeyPatch) -> Mock:
|
|
13
|
+
"""Install a fake Rust backend so tests can exercise selection logic without PyO3."""
|
|
14
|
+
rust_backend = Mock(return_value=b"<rust/>")
|
|
15
|
+
monkeypatch.setattr(fast_module, "_USE_RUST", True)
|
|
16
|
+
monkeypatch.setattr(fast_module, "_rust_dicttoxml", rust_backend)
|
|
17
|
+
return rust_backend
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# @lat: [[tests#Conversion behavior#Fast wrapper uses Rust for supported options]]
|
|
21
|
+
def test_fast_wrapper_uses_rust_when_available_for_supported_options(
|
|
22
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Supported option combinations should go through the Rust callable when present."""
|
|
25
|
+
rust_backend = _force_rust_backend(monkeypatch)
|
|
26
|
+
|
|
27
|
+
result = fast_module.dicttoxml(
|
|
28
|
+
{"name": "Ada"},
|
|
29
|
+
root=False,
|
|
30
|
+
custom_root="person",
|
|
31
|
+
attr_type=False,
|
|
32
|
+
item_wrap=False,
|
|
33
|
+
cdata=True,
|
|
34
|
+
list_headers=True,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
assert result == b"<rust/>"
|
|
38
|
+
rust_backend.assert_called_once_with(
|
|
39
|
+
{"name": "Ada"},
|
|
40
|
+
root=False,
|
|
41
|
+
custom_root="person",
|
|
42
|
+
attr_type=False,
|
|
43
|
+
item_wrap=False,
|
|
44
|
+
cdata=True,
|
|
45
|
+
list_headers=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.parametrize(
|
|
50
|
+
("kwargs", "expected"),
|
|
51
|
+
[
|
|
52
|
+
({"ids": [1]}, b'id="'),
|
|
53
|
+
({"item_func": lambda parent: "entry"}, b"<entry"),
|
|
54
|
+
({"xml_namespaces": {"demo": "https://example.com/demo"}}, b'xmlns:demo="https://example.com/demo"'),
|
|
55
|
+
({"xpath_format": True}, b'xmlns="http://www.w3.org/2005/xpath-functions"'),
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
def test_fast_wrapper_falls_back_to_python_for_unsupported_options(
|
|
59
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
60
|
+
kwargs: dict[str, Any],
|
|
61
|
+
expected: bytes,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Unsupported Rust options should preserve Python semantics instead of calling Rust."""
|
|
64
|
+
rust_backend = _force_rust_backend(monkeypatch)
|
|
65
|
+
|
|
66
|
+
result = fast_module.dicttoxml({"items": [1, 2]}, **kwargs)
|
|
67
|
+
|
|
68
|
+
assert expected in result
|
|
69
|
+
rust_backend.assert_not_called()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# @lat: [[tests#Conversion behavior#Special keys force Python fallback]]
|
|
73
|
+
def test_fast_wrapper_falls_back_to_python_for_special_keys(
|
|
74
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Special @attrs/@val keys require Python processing even when Rust is installed."""
|
|
77
|
+
rust_backend = _force_rust_backend(monkeypatch)
|
|
78
|
+
|
|
79
|
+
result = fast_module.dicttoxml({"record": {"@attrs": {"id": "7"}, "@val": "Ada"}})
|
|
80
|
+
|
|
81
|
+
assert b'id="7"' in result
|
|
82
|
+
assert b">Ada</record>" in result
|
|
83
|
+
rust_backend.assert_not_called()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_fast_wrapper_falls_back_to_python_when_rust_is_unavailable(
|
|
87
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Contributors without json2xml_rs should still exercise the pure Python fallback."""
|
|
90
|
+
rust_backend = Mock(return_value=b"<rust/>")
|
|
91
|
+
monkeypatch.setattr(fast_module, "_USE_RUST", False)
|
|
92
|
+
monkeypatch.setattr(fast_module, "_rust_dicttoxml", rust_backend)
|
|
93
|
+
|
|
94
|
+
result = fast_module.dicttoxml({"name": "Ada"})
|
|
95
|
+
|
|
96
|
+
assert b"<name" in result
|
|
97
|
+
assert b">Ada</name>" in result
|
|
98
|
+
rust_backend.assert_not_called()
|
|
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
|
|
|
31
31
|
class JsonTestHandler(BaseHTTPRequestHandler):
|
|
32
32
|
"""Tiny HTTP handler for exercising the real URL reader."""
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
json_responses: ClassVar[dict[str, tuple[int, bytes]]] = {
|
|
35
35
|
"/data.json": (200, b'{"key": "value", "number": 42}'),
|
|
36
36
|
"/api": (200, b'{"result": "success"}'),
|
|
37
37
|
"/invalid.json": (200, b"invalid json content"),
|
|
@@ -41,7 +41,7 @@ class JsonTestHandler(BaseHTTPRequestHandler):
|
|
|
41
41
|
|
|
42
42
|
def do_GET(self) -> None:
|
|
43
43
|
path = self.path.split("?", 1)[0]
|
|
44
|
-
status, body = self.
|
|
44
|
+
status, body = self.json_responses.get(path, (404, b'{"error": "not found"}'))
|
|
45
45
|
self.send_response(status)
|
|
46
46
|
self.send_header("Content-Type", "application/json")
|
|
47
47
|
self.send_header("Content-Length", str(len(body)))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|