json2xml 6.1.0__tar.gz → 6.2.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.1.0 → json2xml-6.2.0}/HISTORY.rst +11 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/PKG-INFO +4 -3
- json2xml-6.2.0/docs/benchmarks.rst +203 -0
- json2xml-6.2.0/docs/rust_memory_benchmark.rst +110 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/__init__.py +1 -1
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/dicttoxml.py +150 -40
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/dicttoxml_fast.py +10 -9
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/json2xml.py +6 -4
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/utils.py +19 -5
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/PKG-INFO +4 -3
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/SOURCES.txt +2 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/requires.txt +3 -2
- {json2xml-6.1.0 → json2xml-6.2.0}/pyproject.toml +4 -3
- {json2xml-6.1.0 → json2xml-6.2.0}/requirements.in +1 -1
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_dict2xml.py +78 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_dicttoxml_fast_fallback.py +42 -3
- json2xml-6.2.0/tests/test_dicttoxml_unit.py +163 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_json2xml.py +27 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_rust_dicttoxml.py +4 -4
- json2xml-6.1.0/docs/benchmarks.rst +0 -221
- {json2xml-6.1.0 → json2xml-6.2.0}/AUTHORS.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/CONTRIBUTING.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/LICENSE +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/MANIFEST.in +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/README.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/Makefile +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/authors.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/conf.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/contributing.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/history.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/index.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/installation.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/json2xml.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/make.bat +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/modules.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/readme.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/docs/usage.rst +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/cli.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml/types.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/dependency_links.txt +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/entry_points.txt +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/json2xml.egg-info/top_level.txt +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/setup.cfg +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/setup.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/__init__.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/conftest.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_cli.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_missing_coverage.py +0 -0
- {json2xml-6.1.0 → json2xml-6.2.0}/tests/test_utils.py +0 -0
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
|
|
2
|
+
6.2.0 / 2026-06-05
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
* feat: reduce peak serializer memory in the pure Python rooted-output path
|
|
6
|
+
* feat: reduce Rust extension peak serializer memory by writing directly into Python bytes
|
|
7
|
+
* fix: split PyO3 extension-module linking so ``cargo test`` works on macOS
|
|
8
|
+
* chore: release ``json2xml-rs`` 0.3.0 and make ``json2xml[fast]`` require the memory-saving Rust package
|
|
9
|
+
* chore: update the Rust extension crate to Rust 2024 with rust-version 1.96
|
|
10
|
+
* docs: add reproducible Rust memory benchmark results showing about 49% lower serializer RSS delta
|
|
11
|
+
|
|
12
|
+
|
|
2
13
|
6.1.0 / 2026-05-04
|
|
3
14
|
==================
|
|
4
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: json2xml
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.2.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
|
|
@@ -23,15 +23,16 @@ Description-Content-Type: text/x-rst
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
License-File: AUTHORS.rst
|
|
25
25
|
Requires-Dist: defusedxml
|
|
26
|
-
Requires-Dist: urllib3
|
|
26
|
+
Requires-Dist: urllib3>=2.7.0
|
|
27
27
|
Provides-Extra: dev
|
|
28
28
|
Requires-Dist: pytest>=8.4.1; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
30
|
Requires-Dist: coverage; extra == "dev"
|
|
31
31
|
Requires-Dist: setuptools; extra == "dev"
|
|
32
|
+
Requires-Dist: pygments>=2.20.0; extra == "dev"
|
|
32
33
|
Requires-Dist: xmltodict>=0.12.0; extra == "dev"
|
|
33
34
|
Provides-Extra: fast
|
|
34
|
-
Requires-Dist: json2xml-rs>=0.
|
|
35
|
+
Requires-Dist: json2xml-rs>=0.3.0; extra == "fast"
|
|
35
36
|
Dynamic: license-file
|
|
36
37
|
|
|
37
38
|
.. image:: ./json2xml-hero.png
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Benchmarks
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
Comprehensive performance comparison between all json2xml implementations.
|
|
5
|
+
|
|
6
|
+
Test Environment
|
|
7
|
+
----------------
|
|
8
|
+
|
|
9
|
+
* **Machine**: Apple Silicon (arm64)
|
|
10
|
+
* **OS**: macOS 26.5 (Darwin 25.5.0)
|
|
11
|
+
* **Python**: 3.14.4
|
|
12
|
+
* **Date**: May 27, 2026
|
|
13
|
+
* **CLI tools**: ``json2xml-go`` and ``json2xml-zig`` from ``/Users/vinitkumar/.local/bin``
|
|
14
|
+
|
|
15
|
+
Implementations Tested
|
|
16
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
17
|
+
|
|
18
|
+
.. list-table::
|
|
19
|
+
:header-rows: 1
|
|
20
|
+
:widths: 25 20 55
|
|
21
|
+
|
|
22
|
+
* - Implementation
|
|
23
|
+
- Type
|
|
24
|
+
- Notes
|
|
25
|
+
* - Python
|
|
26
|
+
- Library
|
|
27
|
+
- Pure Python ``json2xml``
|
|
28
|
+
* - Rust
|
|
29
|
+
- Library
|
|
30
|
+
- Native extension via PyO3, imported as ``json2xml_rs``
|
|
31
|
+
* - Go
|
|
32
|
+
- CLI
|
|
33
|
+
- Standalone ``json2xml-go`` binary
|
|
34
|
+
* - Zig
|
|
35
|
+
- CLI
|
|
36
|
+
- Standalone ``json2xml-zig`` binary
|
|
37
|
+
|
|
38
|
+
Test Data
|
|
39
|
+
~~~~~~~~~
|
|
40
|
+
|
|
41
|
+
.. list-table::
|
|
42
|
+
:header-rows: 1
|
|
43
|
+
:widths: 20 55 25
|
|
44
|
+
|
|
45
|
+
* - Size
|
|
46
|
+
- Description
|
|
47
|
+
- Bytes
|
|
48
|
+
* - Small
|
|
49
|
+
- Simple object ``{"name": "John", "age": 30, "city": "New York"}``
|
|
50
|
+
- 47
|
|
51
|
+
* - Medium
|
|
52
|
+
- 10 generated records with nested structures
|
|
53
|
+
- 3,215
|
|
54
|
+
* - bigexample.json
|
|
55
|
+
- Real-world patent data
|
|
56
|
+
- 2,018
|
|
57
|
+
* - Large
|
|
58
|
+
- 100 generated records with nested structures
|
|
59
|
+
- 32,206
|
|
60
|
+
* - Very Large
|
|
61
|
+
- 1,000 generated records with nested structures
|
|
62
|
+
- 323,131
|
|
63
|
+
|
|
64
|
+
Results
|
|
65
|
+
-------
|
|
66
|
+
|
|
67
|
+
Performance Summary
|
|
68
|
+
~~~~~~~~~~~~~~~~~~~
|
|
69
|
+
|
|
70
|
+
.. list-table::
|
|
71
|
+
:header-rows: 1
|
|
72
|
+
:widths: 28 18 18 18 18
|
|
73
|
+
|
|
74
|
+
* - Test Case
|
|
75
|
+
- Python
|
|
76
|
+
- Rust
|
|
77
|
+
- Go
|
|
78
|
+
- Zig
|
|
79
|
+
* - Small (47B)
|
|
80
|
+
- 3.19µs
|
|
81
|
+
- 0.86µs
|
|
82
|
+
- 6.05ms
|
|
83
|
+
- 3.08ms
|
|
84
|
+
* - Medium (3.2KB)
|
|
85
|
+
- 214.83µs
|
|
86
|
+
- 18.41µs
|
|
87
|
+
- 5.85ms
|
|
88
|
+
- 3.12ms
|
|
89
|
+
* - bigexample (2KB)
|
|
90
|
+
- 91.20µs
|
|
91
|
+
- 7.32µs
|
|
92
|
+
- 5.76ms
|
|
93
|
+
- 3.08ms
|
|
94
|
+
* - Large (32KB)
|
|
95
|
+
- 2.07ms
|
|
96
|
+
- 175.46µs
|
|
97
|
+
- 5.89ms
|
|
98
|
+
- 3.73ms
|
|
99
|
+
* - Very Large (323KB)
|
|
100
|
+
- 21.20ms
|
|
101
|
+
- 1.48ms
|
|
102
|
+
- 6.82ms
|
|
103
|
+
- 7.82ms
|
|
104
|
+
|
|
105
|
+
Speedup vs Pure Python
|
|
106
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
107
|
+
|
|
108
|
+
.. list-table::
|
|
109
|
+
:header-rows: 1
|
|
110
|
+
:widths: 34 22 22 22
|
|
111
|
+
|
|
112
|
+
* - Test Case
|
|
113
|
+
- Rust
|
|
114
|
+
- Go
|
|
115
|
+
- Zig
|
|
116
|
+
* - Small (47B)
|
|
117
|
+
- **3.7x**
|
|
118
|
+
- 0.0x*
|
|
119
|
+
- 0.0x*
|
|
120
|
+
* - Medium (3.2KB)
|
|
121
|
+
- **11.7x**
|
|
122
|
+
- 0.0x*
|
|
123
|
+
- 0.1x*
|
|
124
|
+
* - bigexample (2KB)
|
|
125
|
+
- **12.5x**
|
|
126
|
+
- 0.0x*
|
|
127
|
+
- 0.0x*
|
|
128
|
+
* - Large (32KB)
|
|
129
|
+
- **11.8x**
|
|
130
|
+
- 0.4x*
|
|
131
|
+
- 0.6x*
|
|
132
|
+
* - Very Large (323KB)
|
|
133
|
+
- **14.4x**
|
|
134
|
+
- **3.1x**
|
|
135
|
+
- **2.7x**
|
|
136
|
+
|
|
137
|
+
*CLI tools have process spawn overhead of about 3-6ms, which dominates for small inputs.*
|
|
138
|
+
|
|
139
|
+
Key Observations
|
|
140
|
+
----------------
|
|
141
|
+
|
|
142
|
+
Rust remains the best option for Python library calls. It avoids process overhead and is about 4-14x faster than the optimized pure Python path in this run.
|
|
143
|
+
|
|
144
|
+
Recent pure Python improvements substantially reduced conversion time. Medium and large inputs are roughly an order of magnitude faster than the April 2026 baseline, so relative Rust speedups are lower even though Rust is still fastest.
|
|
145
|
+
|
|
146
|
+
Go and Zig remain useful for native CLI workflows. They are slower for small and medium inputs because startup dominates, but both beat Python on the 323KB workload when full CLI process time is measured.
|
|
147
|
+
|
|
148
|
+
See ``docs/rust_memory_benchmark.rst`` for the June 2026 Rust peak-memory comparison between the previous extension-boundary copy and the Python bytes-writer implementation.
|
|
149
|
+
|
|
150
|
+
When to Use Each Implementation
|
|
151
|
+
-------------------------------
|
|
152
|
+
|
|
153
|
+
.. list-table::
|
|
154
|
+
:header-rows: 1
|
|
155
|
+
:widths: 40 25 35
|
|
156
|
+
|
|
157
|
+
* - Use Case
|
|
158
|
+
- Recommended
|
|
159
|
+
- Why
|
|
160
|
+
* - Python library calls
|
|
161
|
+
- **Rust**
|
|
162
|
+
- 4-14x faster, no process overhead
|
|
163
|
+
* - Small files via CLI
|
|
164
|
+
- **Zig**
|
|
165
|
+
- Fastest startup among native CLIs in this run
|
|
166
|
+
* - Large files via CLI
|
|
167
|
+
- **Go** or **Zig**
|
|
168
|
+
- Both are faster than Python at 323KB
|
|
169
|
+
* - Batch processing
|
|
170
|
+
- **Go** or **Rust**
|
|
171
|
+
- Choose based on shell vs Python integration
|
|
172
|
+
* - Pure Python required
|
|
173
|
+
- **Python**
|
|
174
|
+
- Always available
|
|
175
|
+
|
|
176
|
+
Running the Benchmarks
|
|
177
|
+
----------------------
|
|
178
|
+
|
|
179
|
+
Run benchmarks from a clean checkout with the project installed in an isolated environment.
|
|
180
|
+
|
|
181
|
+
.. code-block:: bash
|
|
182
|
+
|
|
183
|
+
uv venv
|
|
184
|
+
source .venv/bin/activate
|
|
185
|
+
uv pip install -e .
|
|
186
|
+
python benchmark_all.py
|
|
187
|
+
|
|
188
|
+
For Rust benchmarks, install the extension into the same environment.
|
|
189
|
+
|
|
190
|
+
.. code-block:: bash
|
|
191
|
+
|
|
192
|
+
uv pip install maturin
|
|
193
|
+
cd rust
|
|
194
|
+
maturin develop --release
|
|
195
|
+
cd ..
|
|
196
|
+
|
|
197
|
+
For native CLI benchmarks, install the external tools and verify that the commands are visible.
|
|
198
|
+
|
|
199
|
+
.. code-block:: bash
|
|
200
|
+
|
|
201
|
+
go install github.com/vinitkumar/json2xml-go@latest
|
|
202
|
+
which json2xml-go
|
|
203
|
+
which json2xml-zig
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Rust Memory Benchmark
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
This benchmark compares peak memory usage before and after the Rust serializer started writing directly into Python ``bytes``.
|
|
5
|
+
|
|
6
|
+
Summary
|
|
7
|
+
-------
|
|
8
|
+
|
|
9
|
+
The current branch reduces serializer peak RSS by about **77.44 MiB** on a 100,000-record payload that produces **78.17 MiB** of XML. That is a **49.1% reduction** in serializer memory delta compared with the previous PR commit.
|
|
10
|
+
|
|
11
|
+
.. list-table::
|
|
12
|
+
:header-rows: 1
|
|
13
|
+
:widths: 24 22 22 22 22
|
|
14
|
+
|
|
15
|
+
* - Version
|
|
16
|
+
- Commit
|
|
17
|
+
- Avg peak RSS
|
|
18
|
+
- Avg serializer delta
|
|
19
|
+
- Avg time
|
|
20
|
+
* - Previous
|
|
21
|
+
- ``7dd86b0``
|
|
22
|
+
- 349.15 MiB
|
|
23
|
+
- 157.70 MiB
|
|
24
|
+
- 0.180s
|
|
25
|
+
* - Current
|
|
26
|
+
- ``07d840f``
|
|
27
|
+
- 271.65 MiB
|
|
28
|
+
- 80.26 MiB
|
|
29
|
+
- 0.265s
|
|
30
|
+
|
|
31
|
+
The memory result matches the implementation change: the previous version held roughly one final XML payload in Rust plus one Python ``bytes`` payload, while the current version writes into the Python ``bytes`` object directly.
|
|
32
|
+
|
|
33
|
+
Methodology
|
|
34
|
+
-----------
|
|
35
|
+
|
|
36
|
+
The benchmark uses ``benchmark_memory_rust.py`` with a deterministic generated payload so the Rust fast path can be measured without file parsing or pure-Python fallback behavior.
|
|
37
|
+
|
|
38
|
+
* Machine: Apple Silicon arm64
|
|
39
|
+
* OS: macOS 26.5
|
|
40
|
+
* Python: 3.14.0
|
|
41
|
+
* Build: ``python3 -m maturin develop --release --offline``
|
|
42
|
+
* Payload: 100,000 nested records
|
|
43
|
+
* Input JSON size: 44.31 MiB
|
|
44
|
+
* Output XML size: 78.17 MiB
|
|
45
|
+
* Measurement: process ``ru_maxrss`` after payload creation versus peak after ``json2xml_rs.dicttoxml(payload, attr_type=True)``
|
|
46
|
+
* Sampling: three fresh Python processes per version
|
|
47
|
+
|
|
48
|
+
The baseline RSS is captured after the large Python payload is already built. The reported serializer delta is ``peak_rss - baseline_rss``, which focuses the comparison on output construction rather than payload allocation.
|
|
49
|
+
|
|
50
|
+
Raw Samples
|
|
51
|
+
-----------
|
|
52
|
+
|
|
53
|
+
.. list-table::
|
|
54
|
+
:header-rows: 1
|
|
55
|
+
:widths: 24 18 18 18 18
|
|
56
|
+
|
|
57
|
+
* - Run
|
|
58
|
+
- Baseline RSS
|
|
59
|
+
- Peak RSS
|
|
60
|
+
- Serializer delta
|
|
61
|
+
- Time
|
|
62
|
+
* - previous-release-1
|
|
63
|
+
- 191.53 MiB
|
|
64
|
+
- 349.22 MiB
|
|
65
|
+
- 157.69 MiB
|
|
66
|
+
- 0.182s
|
|
67
|
+
* - previous-release-2
|
|
68
|
+
- 191.39 MiB
|
|
69
|
+
- 349.09 MiB
|
|
70
|
+
- 157.70 MiB
|
|
71
|
+
- 0.179s
|
|
72
|
+
* - previous-release-3
|
|
73
|
+
- 191.44 MiB
|
|
74
|
+
- 349.14 MiB
|
|
75
|
+
- 157.70 MiB
|
|
76
|
+
- 0.178s
|
|
77
|
+
* - current-release-1
|
|
78
|
+
- 191.45 MiB
|
|
79
|
+
- 271.77 MiB
|
|
80
|
+
- 80.31 MiB
|
|
81
|
+
- 0.265s
|
|
82
|
+
* - current-release-2
|
|
83
|
+
- 191.48 MiB
|
|
84
|
+
- 271.64 MiB
|
|
85
|
+
- 80.16 MiB
|
|
86
|
+
- 0.272s
|
|
87
|
+
* - current-release-3
|
|
88
|
+
- 191.25 MiB
|
|
89
|
+
- 271.55 MiB
|
|
90
|
+
- 80.30 MiB
|
|
91
|
+
- 0.258s
|
|
92
|
+
|
|
93
|
+
Tradeoff
|
|
94
|
+
--------
|
|
95
|
+
|
|
96
|
+
The memory improvement comes with a throughput cost in this release benchmark. Average conversion time increased from 0.180s to 0.265s, about **47.5% slower** for this payload.
|
|
97
|
+
|
|
98
|
+
That cost is likely from routing every XML write through ``std::io::Write`` and PyO3's bytes writer. The memory win is substantial for large outputs, but latency-sensitive callers may want more timing data before treating the bytes-writer path as a universal improvement.
|
|
99
|
+
|
|
100
|
+
Reproduction
|
|
101
|
+
------------
|
|
102
|
+
|
|
103
|
+
Run each version in a fresh process after installing the desired Rust extension build.
|
|
104
|
+
|
|
105
|
+
.. code-block:: bash
|
|
106
|
+
|
|
107
|
+
python3 -m maturin develop --release --offline
|
|
108
|
+
python3 benchmark_memory_rust.py --records 100000 --label current-release-1
|
|
109
|
+
|
|
110
|
+
For the previous comparison, install commit ``7dd86b0`` in a temporary worktree, then run the same command from the main checkout so the benchmark script stays identical.
|