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.
Files changed (44) hide show
  1. {json2xml-5.0.4 → json2xml-5.1.0}/HISTORY.rst +7 -0
  2. json2xml-5.1.0/PKG-INFO +281 -0
  3. {json2xml-5.0.4 → json2xml-5.1.0}/README.rst +49 -8
  4. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/__init__.py +1 -1
  5. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/dicttoxml.py +26 -14
  6. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/json2xml.py +2 -2
  7. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml/utils.py +0 -1
  8. json2xml-5.1.0/json2xml.egg-info/PKG-INFO +281 -0
  9. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/SOURCES.txt +3 -2
  10. json2xml-5.1.0/json2xml.egg-info/requires.txt +12 -0
  11. json2xml-5.1.0/pyproject.toml +76 -0
  12. json2xml-5.1.0/setup.py +8 -0
  13. json2xml-5.1.0/tests/conftest.py +80 -0
  14. json2xml-5.1.0/tests/test_dict2xml.py +1145 -0
  15. {json2xml-5.0.4 → json2xml-5.1.0}/tests/test_json2xml.py +45 -30
  16. json2xml-5.1.0/tests/test_utils.py +310 -0
  17. json2xml-5.0.4/PKG-INFO +0 -670
  18. json2xml-5.0.4/json2xml.egg-info/PKG-INFO +0 -670
  19. json2xml-5.0.4/json2xml.egg-info/not-zip-safe +0 -1
  20. json2xml-5.0.4/json2xml.egg-info/requires.txt +0 -2
  21. json2xml-5.0.4/pyproject.toml +0 -19
  22. json2xml-5.0.4/setup.py +0 -53
  23. json2xml-5.0.4/tests/test_dict2xml.py +0 -395
  24. {json2xml-5.0.4 → json2xml-5.1.0}/AUTHORS.rst +0 -0
  25. {json2xml-5.0.4 → json2xml-5.1.0}/CONTRIBUTING.rst +0 -0
  26. {json2xml-5.0.4 → json2xml-5.1.0}/LICENSE +0 -0
  27. {json2xml-5.0.4 → json2xml-5.1.0}/MANIFEST.in +0 -0
  28. {json2xml-5.0.4 → json2xml-5.1.0}/docs/Makefile +0 -0
  29. {json2xml-5.0.4 → json2xml-5.1.0}/docs/authors.rst +0 -0
  30. {json2xml-5.0.4 → json2xml-5.1.0}/docs/conf.py +2 -2
  31. {json2xml-5.0.4 → json2xml-5.1.0}/docs/contributing.rst +0 -0
  32. {json2xml-5.0.4 → json2xml-5.1.0}/docs/history.rst +0 -0
  33. {json2xml-5.0.4 → json2xml-5.1.0}/docs/index.rst +0 -0
  34. {json2xml-5.0.4 → json2xml-5.1.0}/docs/installation.rst +0 -0
  35. {json2xml-5.0.4 → json2xml-5.1.0}/docs/json2xml.rst +0 -0
  36. {json2xml-5.0.4 → json2xml-5.1.0}/docs/make.bat +0 -0
  37. {json2xml-5.0.4 → json2xml-5.1.0}/docs/modules.rst +0 -0
  38. {json2xml-5.0.4 → json2xml-5.1.0}/docs/readme.rst +0 -0
  39. {json2xml-5.0.4 → json2xml-5.1.0}/docs/usage.rst +0 -0
  40. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/dependency_links.txt +0 -0
  41. {json2xml-5.0.4 → json2xml-5.1.0}/json2xml.egg-info/top_level.txt +0 -0
  42. {json2xml-5.0.4 → json2xml-5.1.0}/requirements.in +0 -0
  43. {json2xml-5.0.4 → json2xml-5.1.0}/setup.cfg +0 -0
  44. {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
 
@@ -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
- How to run tests
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
- This is provided by pytest, which is straight forward.
231
+ Using tools directly:
190
232
 
191
233
  .. code-block:: console
192
234
 
193
- virtualenv venv -p $(which python3.9)
194
- pip install -r requirements-dev.txt
195
- python setup.py install
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  __author__ = """Vinit Kumar"""
4
4
  __email__ = "mail@vinitkumar.me"
5
- __version__ = "5.0.4"
5
+ __version__ = "5.1.0"
6
6
 
@@ -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, Dict, Union
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[str],
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 string into an XML element"""
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] = {}, cdata: bool = False
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] = {}, cdata: bool = False
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: dict[str, Any],
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, Dict, Optional
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
@@ -1,6 +1,5 @@
1
1
  """Utility methods for converting XML data to dictionary from various sources."""
2
2
  import json
3
- from typing import Dict, Optional
4
3
 
5
4
  import urllib3
6
5