Skip to content

Commit 58f0f13

Browse files
author
Jeroendevr
authored
escape xml char when having attrs (#144)
* escape xml char when having attrs * Separate the tests
1 parent f186cef commit 58f0f13

File tree

3 files changed

+189
-166
lines changed

3 files changed

+189
-166
lines changed

json2xml/dicttoxml.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,10 @@ def dict2xml_str(
247247
attr = item.pop("@attrs", attr) # update attr with custom @attr if exists
248248
rawitem = item["@val"] if "@val" in item else item
249249
if is_primitive_type(rawitem):
250-
subtree = rawitem
250+
if type(rawitem) == str or numbers.Number:
251+
subtree = escape_xml(rawitem)
252+
else:
253+
subtree = rawitem
251254
else:
252255
# we can not use convert_dict, because rawitem could be non-dict
253256
subtree = convert(
@@ -259,6 +262,7 @@ def dict2xml_str(
259262
return subtree
260263

261264
attrstring = make_attrstring(attr)
265+
262266
return f"<{item_name}{attrstring}>{subtree}</{item_name}>"
263267

264268

tests/test_dict2xml.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import unittest
2+
from json2xml import dicttoxml
3+
4+
5+
class TestDict2xml(unittest.TestCase):
6+
7+
def test_dict2xml_with_namespaces(self):
8+
data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'}
9+
namespaces = {'ns1': 'http://www.google.de/ns1', 'ns2': 'http://www.google.de/ns2'}
10+
result = dicttoxml.dicttoxml(data, attr_type=False, xml_namespaces=namespaces)
11+
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
12+
b'<root xmlns:ns1="http://www.google.de/ns1" xmlns:ns2="http://www.google.de/ns2">' \
13+
b'<ns1:node1>data in namespace 1</ns1:node1>' \
14+
b'<ns2:node2>data in namespace 2</ns2:node2>' \
15+
b'</root>' == result
16+
17+
def test_dict2xml_with_xmlns_namespaces(self):
18+
data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'}
19+
namespaces = {'xmlns': "http://www.w3.org/1999/XSL/Transform"}
20+
result = dicttoxml.dicttoxml(obj=data, attr_type=False, xml_namespaces=namespaces)
21+
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
22+
b'<root xmlns="http://www.w3.org/1999/XSL/Transform">' \
23+
b'<ns1:node1>data in namespace 1</ns1:node1>' \
24+
b'<ns2:node2>data in namespace 2</ns2:node2>' \
25+
b'</root>' == result
26+
27+
def test_dict2xml_with_xsi_location(self):
28+
data = {'bike': 'blue'}
29+
wrapper = 'vehicle'
30+
namespaces = {
31+
'xsi': {
32+
'schemaInstance': "http://www.w3.org/2001/XMLSchema-instance",
33+
'schemaLocation': "https://www.w3schools.com note.xsd"
34+
}
35+
}
36+
result = dicttoxml.dicttoxml(data, custom_root=wrapper, xml_namespaces=namespaces, attr_type=False)
37+
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
38+
b'<vehicle xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' \
39+
b'xsi:schemaLocation="https://www.w3schools.com/ note.xsd">' \
40+
b'<bike>blue</bike>'
41+
b'</vehicle>' == result
42+
43+
def test_dict2xml_xsi_xmlns(self):
44+
data = {'bike': 'blue'}
45+
wrapper = 'vehicle'
46+
xml_namespace = {
47+
'xsd': "https://www.w3schools.com/ note.xsd",
48+
'xmlns': "http://www.google.de/ns1",
49+
'xsi': {
50+
'schemaInstance': "http://www.w3.org/2001/XMLSchema-instance",
51+
'schemaLocation': "https://www.w3schools.com"
52+
},
53+
54+
}
55+
result = dicttoxml.dicttoxml(data, custom_root=wrapper, xml_namespaces=xml_namespace,
56+
attr_type=False).decode()
57+
58+
assert '<?xml version="1.0" encoding="UTF-8" ?>'
59+
'<vehicle xmlns:xsd="https://www.w3schools.com/ note.xsd" xmlns=http://www.google.de/ns1'
60+
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.w3schools.com">'
61+
'<bike>blue</bike></vehicle>' == result
62+
63+
def test_dict2xml_with_flat(self):
64+
data = {'flat_list@flat': [1, 2, 3], 'non_flat_list': [4, 5, 6]}
65+
result = dicttoxml.dicttoxml(data, attr_type=False)
66+
assert b'<?xml version="1.0" encoding="UTF-8" ?>'
67+
b'<root><item>1</item><item>2</item><item>3</item>'
68+
b'<non_flat_list><item>4</item><item>5</item><item>6</item></non_flat_list>'
69+
b'</root>' == result
70+
71+
def test_dict2xml_omit_list(self):
72+
obj = {'list': [
73+
{'bike': 'blue'},
74+
{'wheel': 'black'}
75+
]
76+
}
77+
dicttoxml.dicttoxml(obj, root=False, attr_type=False, item_wrap=False)
78+
assert b'<list><bike>blue</bike><wheel>black</wheel></list>'
79+
80+
def test_dict2xml_with_val_and_custom_attr(self):
81+
# in order to use @attr in non-dict objects, we need to lift into a dict and combine with @val as key
82+
data = {'list1': [1, 2, 3], 'list2': {'@attrs': {'myattr1': 'myval1', 'myattr2': 'myval2'}, '@val': [4, 5, 6]}}
83+
result = dicttoxml.dicttoxml(data, attr_type=False)
84+
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
85+
b'<root><list1><item>1</item><item>2</item><item>3</item></list1>' \
86+
b'<list2 myattr1="myval1" myattr2="myval2"><item>4</item><item>5</item><item>6</item></list2>' \
87+
b'</root>' == result
88+
89+
def test_dict2xml_with_ampersand(self):
90+
dict_without_attrs = {'Bicycles': 'Wheels & Steers'}
91+
root = False
92+
attr_type = False
93+
result = dicttoxml.dicttoxml(
94+
dict_without_attrs, root=root, attr_type=attr_type).decode('UTF-8')
95+
assert '<Bicycles>Wheels &amp; Steers</Bicycles>' == result
96+
97+
def test_dict2xml_with_ampsersand_and_attrs(self):
98+
dict_with_attrs = {'Bicycles': {'@attrs': {'xml:lang': 'nl'}, '@val': 'Wheels & Steers'}}
99+
root = False
100+
attr_type = False
101+
assert '<Bicycles xml:lang="nl">Wheels &amp; Steers</Bicycles>' == dicttoxml.dicttoxml(
102+
dict_with_attrs, root=root, attr_type=attr_type).decode('UTF-8')
103+
104+
def test_make_id(self):
105+
make_id_elem = dicttoxml.make_id("li")
106+
assert 'li' in make_id_elem
107+
108+
def test_get_unique_id(self):
109+
unique_id_elem_1 = dicttoxml.get_unique_id("li")
110+
unique_id_elem_2 = dicttoxml.get_unique_id("li")
111+
unique_id_elem_3 = dicttoxml.get_unique_id("li")
112+
unique_id_elem_4 = dicttoxml.get_unique_id("li")
113+
assert len(list(set({unique_id_elem_1, unique_id_elem_2, unique_id_elem_3, unique_id_elem_4}))) == 4
114+
115+
def test_get_xml_type(self):
116+
assert dicttoxml.get_xml_type("abc") == "str"
117+
assert dicttoxml.get_xml_type(1) == "int"
118+
assert dicttoxml.get_xml_type(True) == "bool"
119+
assert dicttoxml.get_xml_type({}) == "dict"
120+
121+
def test_list_parent_elements(self):
122+
123+
default_item_func = dicttoxml.default_item_func
124+
item = [{'frame_color': 'red'}, {'frame_color': 'green'}]
125+
conList = dicttoxml.convert_list(items=item, attr_type=False, cdata=False, ids=None,
126+
item_func=default_item_func, item_wrap=False, parent='Bike', list_headers=True)
127+
assert f'{"<Bike<frame_color>red</frame_color></Bike>"}'
128+
'{"<Bike<frame_color>green</frame_color></Bike>"}' == conList
129+
130+
def test_dict2xml_str_list_header(self):
131+
from json2xml.dicttoxml import dict2xml_str
132+
item_func = dicttoxml.default_item_func
133+
item = {'frame_color': 'red'}
134+
parent = 'Bike'
135+
xml_str = dict2xml_str(attr_type=False, attr={}, item=item, item_func=item_func,
136+
cdata=False, item_name='item', item_wrap=False, parentIsList=True,
137+
parent=parent, list_headers=True)
138+
139+
assert f'{"<Bike><frame_color>red</frame_color></Bike>"}' == xml_str
140+
141+
def test_list_headers(self):
142+
dict = {"Bike": [
143+
{'frame_color': 'red'},
144+
{'frame_color': 'green'}
145+
]}
146+
result = dicttoxml.dicttoxml(dict, root=False, item_wrap=False, attr_type=False, list_headers=True)
147+
assert b'<Bike><frame_color>red</frame_color></Bike>'
148+
'<Bike><frame_color>green</frame_color></Bike>' == result
149+
150+
def test_list_headers_nested(self):
151+
dict = {"transport": {
152+
"Bike": [
153+
{'frame_color': 'red'},
154+
{'frame_color': 'green'}
155+
]}
156+
}
157+
result = dicttoxml.dicttoxml(dict, root=False, item_wrap=False, attr_type=False, list_headers=True)
158+
assert b'<transport><Bike><frame_color>red</frame_color></Bike>'
159+
b'<Bike><frame_color>green</frame_color></Bike></transport>' == result
160+
161+
def test_list_headers_root(self):
162+
dict = {"Bike": [
163+
{'frame_color': 'red'},
164+
{'frame_color': 'green'}
165+
]}
166+
result = dicttoxml.dicttoxml(dict, root=True, item_wrap=False, attr_type=False, list_headers=True)
167+
assert b'<?xml version="1.0" encoding="UTF-8" ?><root>'
168+
b'<Bike><frame_color>red</frame_color><Bike>'
169+
b'<Bike><frame_color>green</frame_color></Bike></root>' == result
170+
171+
def test_dict2xml_no_root(self):
172+
payload = {'mock': 'payload'}
173+
result = dicttoxml.dicttoxml(payload, attr_type=False, root=False)
174+
assert b'<mock>payload</mock>' == result
175+
176+
def test_dict2xml_with_root(self):
177+
payload = {'mock': 'payload'}
178+
result = dicttoxml.dicttoxml(payload, attr_type=False)
179+
assert b'<?xml version="1.0" encoding="UTF-8" ?><root><mock>payload</mock></root>' == result
180+
181+
def test_dict2xml_with_custom_root(self):
182+
payload = {'mock': 'payload'}
183+
result = dicttoxml.dicttoxml(payload, attr_type=False, custom_root="element")
184+
assert b'<?xml version="1.0" encoding="UTF-8" ?><element><mock>payload</mock></element>' == result

tests/test_json2xml.py

Lines changed: 0 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import json
1313

1414
from json2xml import json2xml
15-
from json2xml import dicttoxml
1615
from json2xml.utils import InvalidDataError, readfromjson, readfromstring, readfromurl, \
1716
JSONReadError, StringReadError, URLReadError
1817

@@ -151,21 +150,6 @@ def test_dicttoxml_bug(self):
151150
old_dict = xmltodict.parse(xmldata)
152151
assert 'response' in old_dict.keys()
153152

154-
def test_dict2xml_no_root(self):
155-
payload = {'mock': 'payload'}
156-
result = dicttoxml.dicttoxml(payload, attr_type=False, root=False)
157-
assert b'<mock>payload</mock>' == result
158-
159-
def test_dict2xml_with_root(self):
160-
payload = {'mock': 'payload'}
161-
result = dicttoxml.dicttoxml(payload, attr_type=False)
162-
assert b'<?xml version="1.0" encoding="UTF-8" ?><root><mock>payload</mock></root>' == result
163-
164-
def test_dict2xml_with_custom_root(self):
165-
payload = {'mock': 'payload'}
166-
result = dicttoxml.dicttoxml(payload, attr_type=False, custom_root="element")
167-
assert b'<?xml version="1.0" encoding="UTF-8" ?><element><mock>payload</mock></element>' == result
168-
169153
def test_bad_data(self):
170154
data = b"!\0a8f"
171155
decoded = data.decode("utf-8")
@@ -199,152 +183,3 @@ def test_read_boolean_data_from_json2(self):
199183
assert dict_from_xml["all"]["string_array"]["item"][0]["#text"] == 'a'
200184
assert dict_from_xml["all"]["string_array"]["item"][1]["#text"] == 'b'
201185
assert dict_from_xml["all"]["string_array"]["item"][2]["#text"] == 'c'
202-
203-
def test_dict2xml_with_namespaces(self):
204-
data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'}
205-
namespaces = {'ns1': 'http://www.google.de/ns1', 'ns2': 'http://www.google.de/ns2'}
206-
result = dicttoxml.dicttoxml(data, attr_type=False, xml_namespaces=namespaces)
207-
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
208-
b'<root xmlns:ns1="http://www.google.de/ns1" xmlns:ns2="http://www.google.de/ns2">' \
209-
b'<ns1:node1>data in namespace 1</ns1:node1>' \
210-
b'<ns2:node2>data in namespace 2</ns2:node2>' \
211-
b'</root>' == result
212-
213-
def test_dict2xml_with_xmlns_namespaces(self):
214-
data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'}
215-
namespaces = {'xmlns': "http://www.w3.org/1999/XSL/Transform"}
216-
result = dicttoxml.dicttoxml(obj=data, attr_type=False, xml_namespaces=namespaces)
217-
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
218-
b'<root xmlns="http://www.w3.org/1999/XSL/Transform">' \
219-
b'<ns1:node1>data in namespace 1</ns1:node1>' \
220-
b'<ns2:node2>data in namespace 2</ns2:node2>' \
221-
b'</root>' == result
222-
223-
def test_dict2xml_with_xsi_location(self):
224-
data = {'bike': 'blue'}
225-
wrapper = 'vehicle'
226-
namespaces = {
227-
'xsi': {
228-
'schemaInstance': "http://www.w3.org/2001/XMLSchema-instance",
229-
'schemaLocation': "https://www.w3schools.com note.xsd"
230-
}
231-
}
232-
result = dicttoxml.dicttoxml(data, custom_root=wrapper, xml_namespaces=namespaces, attr_type=False)
233-
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
234-
b'<vehicle xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' \
235-
b'xsi:schemaLocation="https://www.w3schools.com/ note.xsd">' \
236-
b'<bike>blue</bike>'
237-
b'</vehicle>' == result
238-
239-
def test_dict2xml_xsi_xmlns(self):
240-
data = {'bike': 'blue'}
241-
wrapper = 'vehicle'
242-
xml_namespace = {
243-
'xsd': "https://www.w3schools.com/ note.xsd",
244-
'xmlns': "http://www.google.de/ns1",
245-
'xsi': {
246-
'schemaInstance': "http://www.w3.org/2001/XMLSchema-instance",
247-
'schemaLocation': "https://www.w3schools.com"
248-
},
249-
250-
}
251-
result = dicttoxml.dicttoxml(data, custom_root=wrapper, xml_namespaces=xml_namespace,
252-
attr_type=False).decode()
253-
254-
assert '<?xml version="1.0" encoding="UTF-8" ?>'
255-
'<vehicle xmlns:xsd="https://www.w3schools.com/ note.xsd" xmlns=http://www.google.de/ns1'
256-
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.w3schools.com">'
257-
'<bike>blue</bike></vehicle>' == result
258-
259-
def test_dict2xml_with_flat(self):
260-
data = {'flat_list@flat': [1, 2, 3], 'non_flat_list': [4, 5, 6]}
261-
result = dicttoxml.dicttoxml(data, attr_type=False)
262-
assert b'<?xml version="1.0" encoding="UTF-8" ?>'
263-
b'<root><item>1</item><item>2</item><item>3</item>'
264-
b'<non_flat_list><item>4</item><item>5</item><item>6</item></non_flat_list>'
265-
b'</root>' == result
266-
267-
def test_dict2xml_omit_list(self):
268-
obj = {'list': [
269-
{'bike': 'blue'},
270-
{'wheel': 'black'}
271-
]
272-
}
273-
dicttoxml.dicttoxml(obj, root=False, attr_type=False, item_wrap=False)
274-
assert b'<list><bike>blue</bike><wheel>black</wheel></list>'
275-
276-
def test_dict2xml_with_val_and_custom_attr(self):
277-
# in order to use @attr in non-dict objects, we need to lift into a dict and combine with @val as key
278-
data = {'list1': [1, 2, 3], 'list2': {'@attrs': {'myattr1': 'myval1', 'myattr2': 'myval2'}, '@val': [4, 5, 6]}}
279-
result = dicttoxml.dicttoxml(data, attr_type=False)
280-
assert b'<?xml version="1.0" encoding="UTF-8" ?>' \
281-
b'<root><list1><item>1</item><item>2</item><item>3</item></list1>' \
282-
b'<list2 myattr1="myval1" myattr2="myval2"><item>4</item><item>5</item><item>6</item></list2>' \
283-
b'</root>' == result
284-
285-
def test_make_id(self):
286-
make_id_elem = dicttoxml.make_id("li")
287-
assert 'li' in make_id_elem
288-
289-
def test_get_unique_id(self):
290-
unique_id_elem_1 = dicttoxml.get_unique_id("li")
291-
unique_id_elem_2 = dicttoxml.get_unique_id("li")
292-
unique_id_elem_3 = dicttoxml.get_unique_id("li")
293-
unique_id_elem_4 = dicttoxml.get_unique_id("li")
294-
assert len(list(set({unique_id_elem_1, unique_id_elem_2, unique_id_elem_3, unique_id_elem_4}))) == 4
295-
296-
def test_get_xml_type(self):
297-
assert dicttoxml.get_xml_type("abc") == "str"
298-
assert dicttoxml.get_xml_type(1) == "int"
299-
assert dicttoxml.get_xml_type(True) == "bool"
300-
assert dicttoxml.get_xml_type({}) == "dict"
301-
302-
def test_list_parent_elements(self):
303-
304-
default_item_func = dicttoxml.default_item_func
305-
item = [{'frame_color': 'red'}, {'frame_color': 'green'}]
306-
conList = dicttoxml.convert_list(items=item, attr_type=False, cdata=False, ids=None,
307-
item_func=default_item_func, item_wrap=False, parent='Bike', list_headers=True)
308-
assert f'{"<Bike<frame_color>red</frame_color></Bike>"}'
309-
'{"<Bike<frame_color>green</frame_color></Bike>"}' == conList
310-
311-
def test_dict2xml_str_list_header(self):
312-
from json2xml.dicttoxml import dict2xml_str
313-
item_func = dicttoxml.default_item_func
314-
item = {'frame_color': 'red'}
315-
parent = 'Bike'
316-
xml_str = dict2xml_str(attr_type=False, attr={}, item=item, item_func=item_func,
317-
cdata=False, item_name='item', item_wrap=False, parentIsList=True,
318-
parent=parent, list_headers=True)
319-
320-
assert f'{"<Bike><frame_color>red</frame_color></Bike>"}' == xml_str
321-
322-
def test_list_headers(self):
323-
dict = {"Bike": [
324-
{'frame_color': 'red'},
325-
{'frame_color': 'green'}
326-
]}
327-
result = dicttoxml.dicttoxml(dict, root=False, item_wrap=False, attr_type=False, list_headers=True)
328-
assert b'<Bike><frame_color>red</frame_color></Bike>'
329-
'<Bike><frame_color>green</frame_color></Bike>' == result
330-
331-
def test_list_headers_nested(self):
332-
dict = {"transport": {
333-
"Bike": [
334-
{'frame_color': 'red'},
335-
{'frame_color': 'green'}
336-
]}
337-
}
338-
result = dicttoxml.dicttoxml(dict, root=False, item_wrap=False, attr_type=False, list_headers=True)
339-
assert b'<transport><Bike><frame_color>red</frame_color></Bike>'
340-
b'<Bike><frame_color>green</frame_color></Bike></transport>' == result
341-
342-
def test_list_headers_root(self):
343-
dict = {"Bike": [
344-
{'frame_color': 'red'},
345-
{'frame_color': 'green'}
346-
]}
347-
result = dicttoxml.dicttoxml(dict, root=True, item_wrap=False, attr_type=False, list_headers=True)
348-
assert b'<?xml version="1.0" encoding="UTF-8" ?><root>'
349-
b'<Bike><frame_color>red</frame_color><Bike>'
350-
b'<Bike><frame_color>green</frame_color></Bike></root>' == result

0 commit comments

Comments
 (0)