json2xml 6.5.0__tar.gz → 7.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {json2xml-6.5.0 → json2xml-7.0.0}/CONTRIBUTING.rst +8 -12
  2. {json2xml-6.5.0 → json2xml-7.0.0}/HISTORY.rst +32 -0
  3. {json2xml-6.5.0 → json2xml-7.0.0}/MANIFEST.in +1 -1
  4. {json2xml-6.5.0 → json2xml-7.0.0}/PKG-INFO +33 -22
  5. {json2xml-6.5.0 → json2xml-7.0.0}/README.rst +25 -16
  6. json2xml-7.0.0/docs/benchmarks.rst +480 -0
  7. {json2xml-6.5.0 → json2xml-7.0.0}/docs/conf.py +23 -32
  8. {json2xml-6.5.0 → json2xml-7.0.0}/docs/installation.rst +1 -2
  9. {json2xml-6.5.0 → json2xml-7.0.0}/docs/rust_memory_benchmark.rst +2 -2
  10. {json2xml-6.5.0 → json2xml-7.0.0}/docs/usage.rst +21 -2
  11. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml/__init__.py +1 -1
  12. json2xml-7.0.0/json2xml/backend_selector.py +127 -0
  13. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml/cli.py +12 -9
  14. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml/dicttoxml.py +365 -244
  15. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml/dicttoxml_fast.py +60 -7
  16. json2xml-7.0.0/json2xml/json2xml.py +121 -0
  17. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml/types.py +4 -1
  18. json2xml-7.0.0/json2xml/utils.py +356 -0
  19. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml.egg-info/PKG-INFO +33 -22
  20. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml.egg-info/SOURCES.txt +2 -2
  21. json2xml-7.0.0/json2xml.egg-info/requires.txt +15 -0
  22. {json2xml-6.5.0 → json2xml-7.0.0}/pyproject.toml +20 -8
  23. json2xml-7.0.0/setup.cfg +4 -0
  24. {json2xml-6.5.0 → json2xml-7.0.0}/setup.py +0 -1
  25. {json2xml-6.5.0 → json2xml-7.0.0}/tests/conftest.py +8 -0
  26. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_backend_selector.py +6 -1
  27. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_benchmark_multi_python.py +24 -3
  28. json2xml-7.0.0/tests/test_benchmark_security_hardening.py +84 -0
  29. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_cli.py +117 -51
  30. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_dict2xml.py +267 -106
  31. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_dicttoxml_fast_fallback.py +72 -2
  32. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_dicttoxml_unit.py +223 -53
  33. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_json2xml.py +235 -91
  34. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_missing_coverage.py +9 -2
  35. {json2xml-6.5.0 → json2xml-7.0.0}/tests/test_rust_dicttoxml.py +98 -33
  36. json2xml-7.0.0/tests/test_rust_python_parity.py +262 -0
  37. json2xml-7.0.0/tests/test_utils.py +790 -0
  38. json2xml-6.5.0/docs/benchmarks.rst +0 -203
  39. json2xml-6.5.0/json2xml/backend_selector.py +0 -63
  40. json2xml-6.5.0/json2xml/json2xml.py +0 -65
  41. json2xml-6.5.0/json2xml/utils.py +0 -87
  42. json2xml-6.5.0/json2xml.egg-info/requires.txt +0 -13
  43. json2xml-6.5.0/requirements.in +0 -3
  44. json2xml-6.5.0/setup.cfg +0 -11
  45. json2xml-6.5.0/tests/test_utils.py +0 -291
  46. {json2xml-6.5.0 → json2xml-7.0.0}/AUTHORS.rst +0 -0
  47. {json2xml-6.5.0 → json2xml-7.0.0}/LICENSE +0 -0
  48. {json2xml-6.5.0 → json2xml-7.0.0}/docs/Makefile +0 -0
  49. {json2xml-6.5.0 → json2xml-7.0.0}/docs/authors.rst +0 -0
  50. {json2xml-6.5.0 → json2xml-7.0.0}/docs/contributing.rst +0 -0
  51. {json2xml-6.5.0 → json2xml-7.0.0}/docs/history.rst +0 -0
  52. {json2xml-6.5.0 → json2xml-7.0.0}/docs/index.rst +0 -0
  53. {json2xml-6.5.0 → json2xml-7.0.0}/docs/json2xml.rst +0 -0
  54. {json2xml-6.5.0 → json2xml-7.0.0}/docs/make.bat +0 -0
  55. {json2xml-6.5.0 → json2xml-7.0.0}/docs/modules.rst +0 -0
  56. {json2xml-6.5.0 → json2xml-7.0.0}/docs/readme.rst +0 -0
  57. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml.egg-info/dependency_links.txt +0 -0
  58. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml.egg-info/entry_points.txt +0 -0
  59. {json2xml-6.5.0 → json2xml-7.0.0}/json2xml.egg-info/top_level.txt +0 -0
  60. {json2xml-6.5.0 → json2xml-7.0.0}/tests/__init__.py +0 -0
@@ -80,8 +80,7 @@ Ready to contribute? Here's how to set up `json2xml` for local development.
80
80
  $ cd json2xml/
81
81
  $ uv venv
82
82
  $ source .venv/bin/activate
83
- $ uv pip install -r requirements-dev.txt
84
- $ uv pip install -e .
83
+ $ uv pip install -e ".[dev]"
85
84
 
86
85
  4. Create a branch for local development::
87
86
 
@@ -96,7 +95,7 @@ Ready to contribute? Here's how to set up `json2xml` for local development.
96
95
 
97
96
  # Or individually:
98
97
  $ ruff check json2xml tests
99
- $ mypy json2xml tests
98
+ $ uvx ty check json2xml tests
100
99
  $ pytest tests/
101
100
 
102
101
  6. Commit your changes and push your branch to GitHub::
@@ -140,7 +139,7 @@ Install Rust and maturin::
140
139
 
141
140
  ::
142
141
 
143
- $ python benchmark_rust.py
142
+ $ python benchmarks/benchmark_rust.py
144
143
 
145
144
  **Rust Code Structure**
146
145
 
@@ -182,12 +181,9 @@ To run a subset of tests::
182
181
  Deploying
183
182
  ---------
184
183
 
185
- A reminder for the maintainers on how to deploy.
186
- Make sure all your changes are committed (including an entry in HISTORY.rst).
187
- Then run::
188
-
189
- Update the version in pyproject.toml and json2xml/__init__.py
190
- $ git push
191
- $ git push --tags
184
+ A reminder for maintainers on how to deploy:
192
185
 
193
- Travis will then deploy to PyPI if tests pass.
186
+ #. Update the version in ``pyproject.toml`` and ``json2xml/__init__.py``.
187
+ #. Add the release notes to ``HISTORY.rst``.
188
+ #. Run ``make test-all`` and ``make dist``.
189
+ #. Publish the verified artifacts with ``uv publish dist/*``.
@@ -1,4 +1,36 @@
1
1
 
2
+ 7.0.0 / 2026-08-23
3
+ ==================
4
+
5
+ * breaking: make compact UTF-8 ``bytes`` the default public and CLI output; callers that require pretty Unicode text must pass ``pretty=True`` or ``--pretty``
6
+ * fix: normalize the parent name a ``list_headers`` dict member borrows, so a rootless list of dictionaries no longer emits the malformed empty tag ``<>``
7
+ * fix: make the optional Rust backend byte-identical to the Python serializer for every payload it accepts, correcting CDATA on numbers, nested list shapes, list member tag names, ``type="dict"`` under ``list_headers``, and names beginning with ``xml``
8
+ * fix: keep payloads the Rust backend cannot reproduce exactly -- non-JSON-native values, parser-resolved key names, and normalized root names -- on the Python serializer
9
+ * perf: run the Rust backend's payload gate natively, cutting a 1,000-record conversion from 1.51 ms to 0.49 ms
10
+ * fix: enforce conversion output limits from generated UTF-8 bytes instead of rejecting valid payloads with a conservative estimate
11
+ * fix: reject unsupported falsy objects consistently instead of serializing them as null
12
+ * fix: keep public scalar converter attribute dictionaries unchanged
13
+ * perf: validate common ASCII XML attribute names without constructing a DOM
14
+ * fix: serialize date-like values consistently at the root, which previously rejected ``datetime.time`` while accepting it inside dicts and lists
15
+ * refactor: dispatch every element writer through a single type classifier
16
+ * docs: record the string-returning public converters as a retained compatibility surface
17
+ * perf: indent pretty output while serializing instead of re-tokenizing generated XML, roughly halving pretty conversion time and removing the last XML parsing path from the library
18
+ * chore: release ``json2xml-rs`` 0.5.0 first and require it from ``json2xml[fast]`` for parity-safe accelerated installs
19
+ * chore: align local pytest, pre-commit, release, and Rust CI configuration with the supported toolchain
20
+ * chore: move benchmark scripts under ``benchmarks/`` and make ``pyproject.toml`` plus ``uv.lock`` the only dependency declaration
21
+
22
+
23
+ 6.5.1 / 2026-08-05
24
+ ==================
25
+
26
+ * security: pin URL connections to validated DNS addresses to prevent DNS-rebinding bypasses of private-network blocking
27
+ * security: bound encoded and decoded compressed responses to prevent gzip and deflate resource-exhaustion attacks
28
+ * fix: reject XML 1.0-forbidden characters consistently across Python and Rust serializers instead of emitting invalid XML
29
+ * fix: require an actual boolean for ``allow_private_networks`` and normalize malformed-hostname failures as ``URLReadError``
30
+ * chore: release ``json2xml-rs`` 0.4.3 first and require it from ``json2xml[fast]`` for XML-safe accelerated installs
31
+ * docs: document URL safety controls, XML validation behavior, migration guidance, and the Rust-first release sequence
32
+
33
+
2
34
  6.5.0 / 2026-07-15
3
35
  ==================
4
36
 
@@ -3,7 +3,7 @@ include CONTRIBUTING.rst
3
3
  include HISTORY.rst
4
4
  include LICENSE
5
5
  include README.rst
6
- include requirements.in
6
+ include pyproject.toml
7
7
  recursive-include tests *
8
8
  recursive-exclude * __pycache__
9
9
  recursive-exclude * *.py[co]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: json2xml
3
- Version: 6.5.0
3
+ Version: 7.0.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
@@ -26,13 +26,15 @@ Requires-Dist: defusedxml
26
26
  Requires-Dist: urllib3>=2.7.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: pytest>=8.4.1; extra == "dev"
29
- Requires-Dist: pytest-cov; extra == "dev"
30
- Requires-Dist: coverage; extra == "dev"
31
- Requires-Dist: setuptools; extra == "dev"
29
+ Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
30
+ Requires-Dist: pytest-xdist>=3.8.0; extra == "dev"
31
+ Requires-Dist: coverage>=7.15.4; extra == "dev"
32
+ Requires-Dist: ruff>=0.16.3; extra == "dev"
33
+ Requires-Dist: setuptools>=80.9.0; extra == "dev"
32
34
  Requires-Dist: pygments>=2.20.0; extra == "dev"
33
- Requires-Dist: xmltodict>=0.12.0; extra == "dev"
35
+ Requires-Dist: xmltodict>=0.14.2; extra == "dev"
34
36
  Provides-Extra: fast
35
- Requires-Dist: json2xml-rs>=0.4.2; extra == "fast"
37
+ Requires-Dist: json2xml-rs>=0.5.0; extra == "fast"
36
38
  Dynamic: license-file
37
39
 
38
40
  .. image:: ./json2xml-hero.png
@@ -276,11 +278,30 @@ You can use the json2xml library in the following ways:
276
278
  data = readfromjson("examples/licht.json")
277
279
  print(json2xml.Json2xml(data).to_xml())
278
280
 
281
+ URL reads accept only credential-free HTTP(S), reject redirects and non-public
282
+ destinations by default, pin public connections to their validated DNS address,
283
+ and stop after 10 MiB of encoded or decoded content. Gzip and deflate responses
284
+ honor ``Content-Length`` and are decoded incrementally; other content encodings
285
+ are rejected. Trusted library callers can opt into a private endpoint with the
286
+ boolean ``True`` or choose a smaller limit:
287
+
288
+ .. code-block:: python
289
+
290
+ data = readfromurl(
291
+ "http://127.0.0.1:8000/data.json",
292
+ allow_private_networks=True,
293
+ max_response_bytes=1024 * 1024,
294
+ )
295
+
279
296
 
280
297
  Custom Wrappers and Indentation
281
298
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
282
299
 
283
- By default, a wrapper `all` and pretty `True` is set. However, you can easily change this in your code like this:
300
+ By default, a wrapper `all` and compact output (``pretty=False``) are set. Pretty printing can be enabled explicitly:
301
+
302
+ Conversions also default to a nesting limit of 100, an item limit of 100,000,
303
+ and a 10 MiB XML output limit. Pass ``max_depth``, ``max_items``, or
304
+ ``max_output_bytes`` to choose smaller budgets for untrusted workloads.
284
305
 
285
306
  .. code-block:: python
286
307
 
@@ -434,8 +455,7 @@ This project uses modern Python development practices. Here's how to set up a de
434
455
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
435
456
 
436
457
  # Install dependencies
437
- uv pip install -r requirements-dev.txt
438
- uv pip install -e .
458
+ uv pip install -e ".[dev]"
439
459
 
440
460
  **Running Tests and Checks**
441
461
 
@@ -447,25 +467,16 @@ Using Make (recommended):
447
467
 
448
468
  make test # Run tests with coverage
449
469
  make lint # Run linting with ruff
450
- make typecheck # Run type checking with mypy
470
+ make typecheck # Run type checking with ty
451
471
  make check-all # Run all checks (lint, typecheck, test)
452
472
 
453
- Using the development script:
454
-
455
- .. code-block:: console
456
-
457
- python dev.py # Run all checks
458
- python dev.py test # Run tests only
459
- python dev.py lint # Run linting only
460
- python dev.py typecheck # Run type checking only
461
-
462
473
  Using tools directly:
463
474
 
464
475
  .. code-block:: console
465
476
 
466
477
  pytest --cov=json2xml --cov-report=term -xvs tests -n auto
467
478
  ruff check json2xml tests
468
- mypy json2xml tests
479
+ uvx ty check json2xml tests
469
480
 
470
481
  **Rust Extension Development**
471
482
 
@@ -500,7 +511,7 @@ Running Rust benchmarks:
500
511
  .. code-block:: console
501
512
 
502
513
  # After building the extension
503
- python benchmark_rust.py
514
+ python benchmarks/benchmark_rust.py
504
515
 
505
516
  The Rust code is in ``rust/src/lib.rs`` and uses PyO3 for Python bindings.
506
517
 
@@ -550,7 +561,7 @@ The ``json2xml-py`` command-line tool provides an easy way to convert JSON to XM
550
561
  Conversion Options:
551
562
  -w, --wrapper string Wrapper element name (default "all")
552
563
  -r, --root Include root element (default true)
553
- -p, --pretty Pretty print output (default true)
564
+ -p, --pretty Pretty print output (default false)
554
565
  -t, --type Include type attributes (default true)
555
566
  -i, --item-wrap Wrap list items in <item> elements (default true)
556
567
  -x, --xpath Use XPath 3.1 json-to-xml format
@@ -239,11 +239,30 @@ You can use the json2xml library in the following ways:
239
239
  data = readfromjson("examples/licht.json")
240
240
  print(json2xml.Json2xml(data).to_xml())
241
241
 
242
+ URL reads accept only credential-free HTTP(S), reject redirects and non-public
243
+ destinations by default, pin public connections to their validated DNS address,
244
+ and stop after 10 MiB of encoded or decoded content. Gzip and deflate responses
245
+ honor ``Content-Length`` and are decoded incrementally; other content encodings
246
+ are rejected. Trusted library callers can opt into a private endpoint with the
247
+ boolean ``True`` or choose a smaller limit:
248
+
249
+ .. code-block:: python
250
+
251
+ data = readfromurl(
252
+ "http://127.0.0.1:8000/data.json",
253
+ allow_private_networks=True,
254
+ max_response_bytes=1024 * 1024,
255
+ )
256
+
242
257
 
243
258
  Custom Wrappers and Indentation
244
259
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
245
260
 
246
- By default, a wrapper `all` and pretty `True` is set. However, you can easily change this in your code like this:
261
+ By default, a wrapper `all` and compact output (``pretty=False``) are set. Pretty printing can be enabled explicitly:
262
+
263
+ Conversions also default to a nesting limit of 100, an item limit of 100,000,
264
+ and a 10 MiB XML output limit. Pass ``max_depth``, ``max_items``, or
265
+ ``max_output_bytes`` to choose smaller budgets for untrusted workloads.
247
266
 
248
267
  .. code-block:: python
249
268
 
@@ -397,8 +416,7 @@ This project uses modern Python development practices. Here's how to set up a de
397
416
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
398
417
 
399
418
  # Install dependencies
400
- uv pip install -r requirements-dev.txt
401
- uv pip install -e .
419
+ uv pip install -e ".[dev]"
402
420
 
403
421
  **Running Tests and Checks**
404
422
 
@@ -410,25 +428,16 @@ Using Make (recommended):
410
428
 
411
429
  make test # Run tests with coverage
412
430
  make lint # Run linting with ruff
413
- make typecheck # Run type checking with mypy
431
+ make typecheck # Run type checking with ty
414
432
  make check-all # Run all checks (lint, typecheck, test)
415
433
 
416
- Using the development script:
417
-
418
- .. code-block:: console
419
-
420
- python dev.py # Run all checks
421
- python dev.py test # Run tests only
422
- python dev.py lint # Run linting only
423
- python dev.py typecheck # Run type checking only
424
-
425
434
  Using tools directly:
426
435
 
427
436
  .. code-block:: console
428
437
 
429
438
  pytest --cov=json2xml --cov-report=term -xvs tests -n auto
430
439
  ruff check json2xml tests
431
- mypy json2xml tests
440
+ uvx ty check json2xml tests
432
441
 
433
442
  **Rust Extension Development**
434
443
 
@@ -463,7 +472,7 @@ Running Rust benchmarks:
463
472
  .. code-block:: console
464
473
 
465
474
  # After building the extension
466
- python benchmark_rust.py
475
+ python benchmarks/benchmark_rust.py
467
476
 
468
477
  The Rust code is in ``rust/src/lib.rs`` and uses PyO3 for Python bindings.
469
478
 
@@ -513,7 +522,7 @@ The ``json2xml-py`` command-line tool provides an easy way to convert JSON to XM
513
522
  Conversion Options:
514
523
  -w, --wrapper string Wrapper element name (default "all")
515
524
  -r, --root Include root element (default true)
516
- -p, --pretty Pretty print output (default true)
525
+ -p, --pretty Pretty print output (default false)
517
526
  -t, --type Include type attributes (default true)
518
527
  -i, --item-wrap Wrap list items in <item> elements (default true)
519
528
  -x, --xpath Use XPath 3.1 json-to-xml format