json2xml 5.0.4__tar.gz → 5.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-5.0.4 → json2xml-5.1.0}/HISTORY.rst +7 -0
- json2xml-5.1.0/PKG-INFO +281 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/README.rst +49 -8
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/__init__.py +1 -1
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/dicttoxml.py +26 -14
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/json2xml.py +2 -2
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/utils.py +0 -1
- json2xml-5.1.0/json2xml.egg-info/PKG-INFO +281 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/SOURCES.txt +3 -2
- json2xml-5.1.0/json2xml.egg-info/requires.txt +12 -0
- json2xml-5.1.0/pyproject.toml +76 -0
- json2xml-5.1.0/setup.py +8 -0
- json2xml-5.1.0/tests/conftest.py +80 -0
- json2xml-5.1.0/tests/test_dict2xml.py +1145 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/tests/test_json2xml.py +45 -30
- json2xml-5.1.0/tests/test_utils.py +310 -0
- json2xml-5.0.4/PKG-INFO +0 -670
- json2xml-5.0.4/json2xml.egg-info/PKG-INFO +0 -670
- json2xml-5.0.4/json2xml.egg-info/not-zip-safe +0 -1
- json2xml-5.0.4/json2xml.egg-info/requires.txt +0 -2
- json2xml-5.0.4/pyproject.toml +0 -19
- json2xml-5.0.4/setup.py +0 -53
- json2xml-5.0.4/tests/test_dict2xml.py +0 -395
- {json2xml-5.0.4 → json2xml-5.1.0}/AUTHORS.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/CONTRIBUTING.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/LICENSE +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/MANIFEST.in +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/Makefile +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/authors.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/conf.py +2 -2
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/contributing.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/history.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/index.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/installation.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/json2xml.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/make.bat +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/modules.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/readme.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/docs/usage.rst +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/dependency_links.txt +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/top_level.txt +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/requirements.in +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/setup.cfg +0 -0
- {json2xml-5.0.4 → json2xml-5.1.0}/tests/__init__.py +0 -0
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
History
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
5.0.5 / 2024-08-31
|
|
5
|
+
==================
|
|
6
|
+
|
|
7
|
+
* tests (json2xml): add test for encoding
|
|
8
|
+
* fix missing encoding indication on pretty mode (#213)
|
|
9
|
+
* chore(deps): bump webob from 1.8.7 to 1.8.8 in /docs (#212)
|
|
10
|
+
|
|
4
11
|
5.0.4 / 2024-08-08
|
|
5
12
|
==================
|
|
6
13
|
|
json2xml-5.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: json2xml
|
|
3
|
+
Version: 5.1.0
|
|
4
|
+
Summary: Simple Python Library to convert JSON to XML
|
|
5
|
+
Author-email: Vinit Kumar <mail@vinitkumar.me>
|
|
6
|
+
License: Apache Software License 2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/vinitkumar/json2xml
|
|
8
|
+
Keywords: json2xml
|
|
9
|
+
Classifier: Development Status :: 6 - Mature
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/x-rst
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: AUTHORS.rst
|
|
25
|
+
Requires-Dist: defusedxml
|
|
26
|
+
Requires-Dist: urllib3
|
|
27
|
+
Requires-Dist: xmltodict>=0.12.0
|
|
28
|
+
Requires-Dist: pytest
|
|
29
|
+
Requires-Dist: pytest-cov
|
|
30
|
+
Requires-Dist: coverage
|
|
31
|
+
Requires-Dist: py
|
|
32
|
+
Requires-Dist: setuptools
|
|
33
|
+
Provides-Extra: test
|
|
34
|
+
Requires-Dist: pytest==7.0.1; extra == "test"
|
|
35
|
+
Requires-Dist: py==1.11.0; extra == "test"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
========
|
|
39
|
+
json2xml
|
|
40
|
+
========
|
|
41
|
+
|
|
42
|
+
.. image:: https://badge.fury.io/py/json2xml.svg
|
|
43
|
+
.. image:: https://static.pepy.tech/personalized-badge/json2xml?period=total&units=international_system&left_color=blue&right_color=orange&left_text=Downloads
|
|
44
|
+
:target: https://pepy.tech/project/json2xml
|
|
45
|
+
|
|
46
|
+
.. image:: https://github.com/vinitkumar/json2xml/actions/workflows/pythonpackage.yml/badge.svg
|
|
47
|
+
.. image:: https://img.shields.io/pypi/pyversions/json2xml.svg
|
|
48
|
+
.. image:: https://readthedocs.org/projects/json2xml/badge/?version=latest
|
|
49
|
+
:target: https://json2xml.readthedocs.io/en/latest/?badge=latest
|
|
50
|
+
:alt: Documentation Status
|
|
51
|
+
.. image:: https://codecov.io/gh/vinitkumar/json2xml/branch/master/graph/badge.svg?token=Yt2h55eTL2
|
|
52
|
+
:target: https://codecov.io/gh/vinitkumar/json2xml
|
|
53
|
+
|
|
54
|
+
json2xml is a Python library that allows you to convert JSON data into XML format. It's simple, efficient, and easy to use.
|
|
55
|
+
|
|
56
|
+
Documentation: https://json2xml.readthedocs.io.
|
|
57
|
+
|
|
58
|
+
The library was initially dependent on the `dict2xml` project, but it has now been integrated into json2xml itself. This has led to cleaner code, the addition of types and tests, and overall improved performance.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Architecture Diagram
|
|
63
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
64
|
+
|
|
65
|
+
.. image:: ./diagram.png
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Features
|
|
69
|
+
^^^^^^^^
|
|
70
|
+
|
|
71
|
+
json2xml supports the following features:
|
|
72
|
+
|
|
73
|
+
* Conversion from a `json` string to XML
|
|
74
|
+
* Conversion from a `json` file to XML
|
|
75
|
+
* Conversion from an API that emits `json` data to XML
|
|
76
|
+
|
|
77
|
+
Usage
|
|
78
|
+
^^^^^
|
|
79
|
+
|
|
80
|
+
You can use the json2xml library in the following ways:
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
.. code-block:: python
|
|
84
|
+
|
|
85
|
+
from json2xml import json2xml
|
|
86
|
+
from json2xml.utils import readfromurl, readfromstring, readfromjson
|
|
87
|
+
|
|
88
|
+
# Convert JSON data from a URL to XML
|
|
89
|
+
data = readfromurl("https://api.publicapis.org/entries")
|
|
90
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
91
|
+
|
|
92
|
+
# Convert a JSON string to XML
|
|
93
|
+
data = readfromstring(
|
|
94
|
+
'{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}'
|
|
95
|
+
)
|
|
96
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
97
|
+
|
|
98
|
+
# Convert a JSON file to XML
|
|
99
|
+
data = readfromjson("examples/licht.json")
|
|
100
|
+
print(json2xml.Json2xml(data).to_xml())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
Custom Wrappers and Indentation
|
|
104
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
105
|
+
|
|
106
|
+
By default, a wrapper `all` and pretty `True` is set. However, you can easily change this in your code like this:
|
|
107
|
+
|
|
108
|
+
.. code-block:: python
|
|
109
|
+
|
|
110
|
+
from json2xml import json2xml
|
|
111
|
+
from json2xml.utils import readfromurl, readfromstring, readfromjson
|
|
112
|
+
|
|
113
|
+
data = readfromstring(
|
|
114
|
+
'{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}'
|
|
115
|
+
)
|
|
116
|
+
print(json2xml.Json2xml(data, wrapper="all", pretty=True).to_xml())
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
Outputs this:
|
|
120
|
+
|
|
121
|
+
.. code-block:: xml
|
|
122
|
+
|
|
123
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
124
|
+
<all>
|
|
125
|
+
<login type="str">mojombo</login>
|
|
126
|
+
<id type="int">1</id>
|
|
127
|
+
<avatar_url type="str">https://avatars0.githubusercontent.com/u/1?v=4</avatar_url>
|
|
128
|
+
</all>
|
|
129
|
+
|
|
130
|
+
Omit List item
|
|
131
|
+
^^^^^^^^^^^^^^
|
|
132
|
+
|
|
133
|
+
Assume the following json input
|
|
134
|
+
|
|
135
|
+
.. code-block:: json
|
|
136
|
+
|
|
137
|
+
{
|
|
138
|
+
"my_items": [
|
|
139
|
+
{ "my_item": { "id": 1 } },
|
|
140
|
+
{ "my_item": { "id": 2 } }
|
|
141
|
+
],
|
|
142
|
+
"my_str_items": ["a", "b"]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
By default, items in an array are wrapped in <item></item>.
|
|
146
|
+
|
|
147
|
+
Default output:
|
|
148
|
+
|
|
149
|
+
.. code-block:: xml
|
|
150
|
+
|
|
151
|
+
<?xml version="1.0" ?>
|
|
152
|
+
<all>
|
|
153
|
+
<my_items type="list">
|
|
154
|
+
<item type="dict">
|
|
155
|
+
<my_item type="dict">
|
|
156
|
+
<id type="int">1</id>
|
|
157
|
+
</my_item>
|
|
158
|
+
</item>
|
|
159
|
+
<item type="dict">
|
|
160
|
+
<my_item type="dict">
|
|
161
|
+
<id type="int">2</id>
|
|
162
|
+
</my_item>
|
|
163
|
+
</item>
|
|
164
|
+
</my_items>
|
|
165
|
+
<my_str_items type="list">
|
|
166
|
+
<item type="str">a</item>
|
|
167
|
+
<item type="str">b</item>
|
|
168
|
+
</my_str_items>
|
|
169
|
+
<empty type="list"/>
|
|
170
|
+
</all>
|
|
171
|
+
|
|
172
|
+
However, you can change this behavior using the item_wrap property like this:
|
|
173
|
+
|
|
174
|
+
.. code-block:: python
|
|
175
|
+
|
|
176
|
+
from json2xml import json2xml
|
|
177
|
+
from json2xml.utils import readfromurl, readfromstring, readfromjson
|
|
178
|
+
|
|
179
|
+
data = readfromstring('{"my_items":[{"my_item":{"id":1} },{"my_item":{"id":2} }],"my_str_items":["a","b"]}')
|
|
180
|
+
print(json2xml.Json2xml(data, item_wrap=False).to_xml())
|
|
181
|
+
|
|
182
|
+
Outputs this:
|
|
183
|
+
|
|
184
|
+
.. code-block:: xml
|
|
185
|
+
|
|
186
|
+
<?xml version="1.0" ?>
|
|
187
|
+
<all>
|
|
188
|
+
<my_items type="list">
|
|
189
|
+
<my_item type="dict">
|
|
190
|
+
<id type="int">1</id>
|
|
191
|
+
</my_item>
|
|
192
|
+
<my_item type="dict">
|
|
193
|
+
<id type="int">2</id>
|
|
194
|
+
</my_item>
|
|
195
|
+
</my_items>
|
|
196
|
+
<my_str_items type="str">a</my_str_items>
|
|
197
|
+
<my_str_items type="str">b</my_str_items>
|
|
198
|
+
</all>
|
|
199
|
+
|
|
200
|
+
Optional Attribute Type Support
|
|
201
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
202
|
+
|
|
203
|
+
You can also specify if the output XML needs to have type specified or not. Here is the usage:
|
|
204
|
+
|
|
205
|
+
.. code-block:: python
|
|
206
|
+
|
|
207
|
+
from json2xml import json2xml
|
|
208
|
+
from json2xml.utils import readfromurl, readfromstring, readfromjson
|
|
209
|
+
|
|
210
|
+
data = readfromstring(
|
|
211
|
+
'{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}'
|
|
212
|
+
)
|
|
213
|
+
print(json2xml.Json2xml(data, wrapper="all", pretty=True, attr_type=False).to_xml())
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
Outputs this:
|
|
217
|
+
|
|
218
|
+
.. code-block:: xml
|
|
219
|
+
|
|
220
|
+
<?xml version="1.0" ?>
|
|
221
|
+
<all>
|
|
222
|
+
<login>mojombo</login>
|
|
223
|
+
<id>1</id>
|
|
224
|
+
<avatar_url>https://avatars0.githubusercontent.com/u/1?v=4</avatar_url>
|
|
225
|
+
</all>
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
The methods are simple and easy to use and there are also checks inside of code to exit cleanly
|
|
229
|
+
in case any of the input(file, string or API URL) returns invalid JSON.
|
|
230
|
+
|
|
231
|
+
Development
|
|
232
|
+
^^^^^^^^^^^
|
|
233
|
+
|
|
234
|
+
This project uses modern Python development practices. Here's how to set up a development environment:
|
|
235
|
+
|
|
236
|
+
.. code-block:: console
|
|
237
|
+
|
|
238
|
+
# Create and activate virtual environment (using uv - recommended)
|
|
239
|
+
uv venv
|
|
240
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
241
|
+
|
|
242
|
+
# Install dependencies
|
|
243
|
+
uv pip install -r requirements-dev.txt
|
|
244
|
+
uv pip install -e .
|
|
245
|
+
|
|
246
|
+
**Running Tests and Checks**
|
|
247
|
+
|
|
248
|
+
We provide several ways to run tests and quality checks:
|
|
249
|
+
|
|
250
|
+
Using Make (recommended):
|
|
251
|
+
|
|
252
|
+
.. code-block:: console
|
|
253
|
+
|
|
254
|
+
make test # Run tests with coverage
|
|
255
|
+
make lint # Run linting with ruff
|
|
256
|
+
make typecheck # Run type checking with mypy
|
|
257
|
+
make check-all # Run all checks (lint, typecheck, test)
|
|
258
|
+
|
|
259
|
+
Using the development script:
|
|
260
|
+
|
|
261
|
+
.. code-block:: console
|
|
262
|
+
|
|
263
|
+
python dev.py # Run all checks
|
|
264
|
+
python dev.py test # Run tests only
|
|
265
|
+
python dev.py lint # Run linting only
|
|
266
|
+
python dev.py typecheck # Run type checking only
|
|
267
|
+
|
|
268
|
+
Using tools directly:
|
|
269
|
+
|
|
270
|
+
.. code-block:: console
|
|
271
|
+
|
|
272
|
+
pytest --cov=json2xml --cov-report=term -xvs tests -n auto
|
|
273
|
+
ruff check json2xml tests
|
|
274
|
+
mypy json2xml tests
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
Help and Support to maintain this project
|
|
278
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
279
|
+
|
|
280
|
+
- You can sponsor my work for this plugin here: https://github.com/sponsors/vinitkumar/
|
|
281
|
+
|
|
@@ -20,6 +20,14 @@ Documentation: https://json2xml.readthedocs.io.
|
|
|
20
20
|
|
|
21
21
|
The library was initially dependent on the `dict2xml` project, but it has now been integrated into json2xml itself. This has led to cleaner code, the addition of types and tests, and overall improved performance.
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Architecture Diagram
|
|
26
|
+
^^^^^^^^^^^^^^^^^^^^
|
|
27
|
+
|
|
28
|
+
.. image:: ./diagram.png
|
|
29
|
+
|
|
30
|
+
|
|
23
31
|
Features
|
|
24
32
|
^^^^^^^^
|
|
25
33
|
|
|
@@ -75,7 +83,7 @@ Outputs this:
|
|
|
75
83
|
|
|
76
84
|
.. code-block:: xml
|
|
77
85
|
|
|
78
|
-
<?xml version="1.0" ?>
|
|
86
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
79
87
|
<all>
|
|
80
88
|
<login type="str">mojombo</login>
|
|
81
89
|
<id type="int">1</id>
|
|
@@ -183,17 +191,50 @@ Outputs this:
|
|
|
183
191
|
The methods are simple and easy to use and there are also checks inside of code to exit cleanly
|
|
184
192
|
in case any of the input(file, string or API URL) returns invalid JSON.
|
|
185
193
|
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
Development
|
|
195
|
+
^^^^^^^^^^^
|
|
196
|
+
|
|
197
|
+
This project uses modern Python development practices. Here's how to set up a development environment:
|
|
198
|
+
|
|
199
|
+
.. code-block:: console
|
|
200
|
+
|
|
201
|
+
# Create and activate virtual environment (using uv - recommended)
|
|
202
|
+
uv venv
|
|
203
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
204
|
+
|
|
205
|
+
# Install dependencies
|
|
206
|
+
uv pip install -r requirements-dev.txt
|
|
207
|
+
uv pip install -e .
|
|
208
|
+
|
|
209
|
+
**Running Tests and Checks**
|
|
210
|
+
|
|
211
|
+
We provide several ways to run tests and quality checks:
|
|
212
|
+
|
|
213
|
+
Using Make (recommended):
|
|
214
|
+
|
|
215
|
+
.. code-block:: console
|
|
216
|
+
|
|
217
|
+
make test # Run tests with coverage
|
|
218
|
+
make lint # Run linting with ruff
|
|
219
|
+
make typecheck # Run type checking with mypy
|
|
220
|
+
make check-all # Run all checks (lint, typecheck, test)
|
|
221
|
+
|
|
222
|
+
Using the development script:
|
|
223
|
+
|
|
224
|
+
.. code-block:: console
|
|
225
|
+
|
|
226
|
+
python dev.py # Run all checks
|
|
227
|
+
python dev.py test # Run tests only
|
|
228
|
+
python dev.py lint # Run linting only
|
|
229
|
+
python dev.py typecheck # Run type checking only
|
|
188
230
|
|
|
189
|
-
|
|
231
|
+
Using tools directly:
|
|
190
232
|
|
|
191
233
|
.. code-block:: console
|
|
192
234
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
pytest -vv
|
|
235
|
+
pytest --cov=json2xml --cov-report=term -xvs tests -n auto
|
|
236
|
+
ruff check json2xml tests
|
|
237
|
+
mypy json2xml tests
|
|
197
238
|
|
|
198
239
|
|
|
199
240
|
Help and Support to maintain this project
|
|
@@ -5,12 +5,11 @@ import logging
|
|
|
5
5
|
import numbers
|
|
6
6
|
from collections.abc import Callable, Sequence
|
|
7
7
|
from random import SystemRandom
|
|
8
|
-
from typing import Any,
|
|
8
|
+
from typing import Any, Union
|
|
9
9
|
|
|
10
10
|
from defusedxml.minidom import parseString
|
|
11
11
|
|
|
12
12
|
# Create a safe random number generator
|
|
13
|
-
safe_random = SystemRandom()
|
|
14
13
|
|
|
15
14
|
# Set up logging
|
|
16
15
|
LOG = logging.getLogger("dicttoxml")
|
|
@@ -28,6 +27,7 @@ def make_id(element: str, start: int = 100000, end: int = 999999) -> str:
|
|
|
28
27
|
Returns:
|
|
29
28
|
str: The generated ID.
|
|
30
29
|
"""
|
|
30
|
+
safe_random = SystemRandom()
|
|
31
31
|
return f"{element}_{safe_random.randint(start, end)}"
|
|
32
32
|
|
|
33
33
|
|
|
@@ -59,7 +59,7 @@ ELEMENT = Union[
|
|
|
59
59
|
float,
|
|
60
60
|
bool,
|
|
61
61
|
numbers.Number,
|
|
62
|
-
Sequence[
|
|
62
|
+
Sequence[Any],
|
|
63
63
|
datetime.datetime,
|
|
64
64
|
datetime.date,
|
|
65
65
|
None,
|
|
@@ -97,7 +97,7 @@ def get_xml_type(val: ELEMENT) -> str:
|
|
|
97
97
|
return type(val).__name__
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
def escape_xml(s: str | numbers.Number) -> str:
|
|
100
|
+
def escape_xml(s: str | int | float | numbers.Number) -> str:
|
|
101
101
|
"""
|
|
102
102
|
Escape a string for use in XML.
|
|
103
103
|
|
|
@@ -127,7 +127,7 @@ def make_attrstring(attr: dict[str, Any]) -> str:
|
|
|
127
127
|
Returns:
|
|
128
128
|
str: The string of XML attributes.
|
|
129
129
|
"""
|
|
130
|
-
attrstring = " ".join([f'{k}="{v}"' for k, v in attr.items()])
|
|
130
|
+
attrstring = " ".join([f'{k}="{escape_xml(v)}"' for k, v in attr.items()])
|
|
131
131
|
return f'{" " if attrstring != "" else ""}{attrstring}'
|
|
132
132
|
|
|
133
133
|
|
|
@@ -178,7 +178,7 @@ def make_valid_xml_name(key: str, attr: dict[str, Any]) -> tuple[str, dict[str,
|
|
|
178
178
|
return key, attr
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
def wrap_cdata(s: str | numbers.Number) -> str:
|
|
181
|
+
def wrap_cdata(s: str | int | float | numbers.Number) -> str:
|
|
182
182
|
"""Wraps a string into CDATA sections"""
|
|
183
183
|
s = str(s).replace("]]>", "]]]]><![CDATA[>")
|
|
184
184
|
return "<![CDATA[" + s + "]]>"
|
|
@@ -263,16 +263,17 @@ def dict2xml_str(
|
|
|
263
263
|
"""
|
|
264
264
|
ids: list[str] = [] # initialize list of unique ids
|
|
265
265
|
", ".join(str(key) for key in item)
|
|
266
|
+
subtree = "" # Initialize subtree with default empty string
|
|
266
267
|
|
|
267
268
|
if attr_type:
|
|
268
269
|
attr["type"] = get_xml_type(item)
|
|
269
270
|
val_attr: dict[str, str] = item.pop("@attrs", attr) # update attr with custom @attr if exists
|
|
270
271
|
rawitem = item["@val"] if "@val" in item else item
|
|
271
272
|
if is_primitive_type(rawitem):
|
|
273
|
+
if isinstance(rawitem, dict):
|
|
274
|
+
subtree = escape_xml(str(rawitem))
|
|
272
275
|
if isinstance(rawitem, str):
|
|
273
276
|
subtree = escape_xml(rawitem)
|
|
274
|
-
else:
|
|
275
|
-
subtree = rawitem
|
|
276
277
|
else:
|
|
277
278
|
# we can not use convert_dict, because rawitem could be non-dict
|
|
278
279
|
subtree = convert(
|
|
@@ -306,6 +307,7 @@ def list2xml_str(
|
|
|
306
307
|
if attr_type:
|
|
307
308
|
attr["type"] = get_xml_type(item)
|
|
308
309
|
flat = False
|
|
310
|
+
subtree = "" # Initialize subtree with default empty string
|
|
309
311
|
if item_name.endswith("@flat"):
|
|
310
312
|
item_name = item_name[0:-5]
|
|
311
313
|
flat = True
|
|
@@ -503,14 +505,20 @@ def convert_list(
|
|
|
503
505
|
|
|
504
506
|
def convert_kv(
|
|
505
507
|
key: str,
|
|
506
|
-
val: str | numbers.Number,
|
|
508
|
+
val: str | int | float | numbers.Number | datetime.datetime | datetime.date,
|
|
507
509
|
attr_type: bool,
|
|
508
|
-
attr: dict[str, Any] =
|
|
510
|
+
attr: dict[str, Any] | None = None,
|
|
509
511
|
cdata: bool = False,
|
|
510
512
|
) -> str:
|
|
511
|
-
"""Converts a number or
|
|
513
|
+
"""Converts a number, string, or datetime into an XML element"""
|
|
514
|
+
if attr is None:
|
|
515
|
+
attr = {}
|
|
512
516
|
key, attr = make_valid_xml_name(key, attr)
|
|
513
517
|
|
|
518
|
+
# Convert datetime to isoformat string
|
|
519
|
+
if hasattr(val, "isoformat") and isinstance(val, (datetime.datetime, datetime.date)):
|
|
520
|
+
val = val.isoformat()
|
|
521
|
+
|
|
514
522
|
if attr_type:
|
|
515
523
|
attr["type"] = get_xml_type(val)
|
|
516
524
|
attr_string = make_attrstring(attr)
|
|
@@ -518,9 +526,11 @@ def convert_kv(
|
|
|
518
526
|
|
|
519
527
|
|
|
520
528
|
def convert_bool(
|
|
521
|
-
key: str, val: bool, attr_type: bool, attr: dict[str, Any] =
|
|
529
|
+
key: str, val: bool, attr_type: bool, attr: dict[str, Any] | None = None, cdata: bool = False
|
|
522
530
|
) -> str:
|
|
523
531
|
"""Converts a boolean into an XML element"""
|
|
532
|
+
if attr is None:
|
|
533
|
+
attr = {}
|
|
524
534
|
key, attr = make_valid_xml_name(key, attr)
|
|
525
535
|
|
|
526
536
|
if attr_type:
|
|
@@ -530,9 +540,11 @@ def convert_bool(
|
|
|
530
540
|
|
|
531
541
|
|
|
532
542
|
def convert_none(
|
|
533
|
-
key: str, attr_type: bool, attr: dict[str, Any] =
|
|
543
|
+
key: str, attr_type: bool, attr: dict[str, Any] | None = None, cdata: bool = False
|
|
534
544
|
) -> str:
|
|
535
545
|
"""Converts a null value into an XML element"""
|
|
546
|
+
if attr is None:
|
|
547
|
+
attr = {}
|
|
536
548
|
key, attr = make_valid_xml_name(key, attr)
|
|
537
549
|
|
|
538
550
|
if attr_type:
|
|
@@ -542,7 +554,7 @@ def convert_none(
|
|
|
542
554
|
|
|
543
555
|
|
|
544
556
|
def dicttoxml(
|
|
545
|
-
obj:
|
|
557
|
+
obj: ELEMENT,
|
|
546
558
|
root: bool = True,
|
|
547
559
|
custom_root: str = "root",
|
|
548
560
|
ids: list[int] | None = None,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pyexpat import ExpatError
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
from defusedxml.minidom import parseString
|
|
5
5
|
|
|
@@ -42,7 +42,7 @@ class Json2xml:
|
|
|
42
42
|
)
|
|
43
43
|
if self.pretty:
|
|
44
44
|
try:
|
|
45
|
-
result = parseString(xml_data).toprettyxml()
|
|
45
|
+
result = parseString(xml_data).toprettyxml(encoding="UTF-8").decode()
|
|
46
46
|
except ExpatError:
|
|
47
47
|
raise InvalidDataError
|
|
48
48
|
return result
|