test_dicttoxml.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import sys
  2. from xmltodict import parse, unparse
  3. from collections import OrderedDict
  4. import unittest
  5. import re
  6. from textwrap import dedent
  7. IS_JYTHON = sys.platform.startswith('java')
  8. _HEADER_RE = re.compile(r'^[^\n]*\n')
  9. def _strip(fullxml):
  10. return _HEADER_RE.sub('', fullxml)
  11. class DictToXMLTestCase(unittest.TestCase):
  12. def test_root(self):
  13. obj = {'a': None}
  14. self.assertEqual(obj, parse(unparse(obj)))
  15. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  16. def test_simple_cdata(self):
  17. obj = {'a': 'b'}
  18. self.assertEqual(obj, parse(unparse(obj)))
  19. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  20. def test_cdata(self):
  21. obj = {'a': {'#text': 'y'}}
  22. self.assertEqual(obj, parse(unparse(obj), force_cdata=True))
  23. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  24. def test_attrib(self):
  25. obj = {'a': {'@href': 'x'}}
  26. self.assertEqual(obj, parse(unparse(obj)))
  27. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  28. def test_attrib_and_cdata(self):
  29. obj = {'a': {'@href': 'x', '#text': 'y'}}
  30. self.assertEqual(obj, parse(unparse(obj)))
  31. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  32. def test_list(self):
  33. obj = {'a': {'b': ['1', '2', '3']}}
  34. self.assertEqual(obj, parse(unparse(obj)))
  35. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  36. def test_list_expand_iter(self):
  37. obj = {'a': {'b': [['1', '2'], ['3',]]}}
  38. #self.assertEqual(obj, parse(unparse(obj, expand_iter="item")))
  39. exp_xml = dedent('''\
  40. <?xml version="1.0" encoding="utf-8"?>
  41. <a><b><item>1</item><item>2</item></b><b><item>3</item></b></a>''')
  42. self.assertEqual(exp_xml, unparse(obj, expand_iter="item"))
  43. def test_generator(self):
  44. obj = {'a': {'b': ['1', '2', '3']}}
  45. def lazy_obj():
  46. return {'a': {'b': (i for i in ('1', '2', '3'))}}
  47. self.assertEqual(obj, parse(unparse(lazy_obj())))
  48. self.assertEqual(unparse(lazy_obj()),
  49. unparse(parse(unparse(lazy_obj()))))
  50. def test_no_root(self):
  51. self.assertRaises(ValueError, unparse, {})
  52. def test_multiple_roots(self):
  53. self.assertRaises(ValueError, unparse, {'a': '1', 'b': '2'})
  54. self.assertRaises(ValueError, unparse, {'a': ['1', '2', '3']})
  55. def test_no_root_nofulldoc(self):
  56. self.assertEqual(unparse({}, full_document=False), '')
  57. def test_multiple_roots_nofulldoc(self):
  58. obj = OrderedDict((('a', 1), ('b', 2)))
  59. xml = unparse(obj, full_document=False)
  60. self.assertEqual(xml, '<a>1</a><b>2</b>')
  61. obj = {'a': [1, 2]}
  62. xml = unparse(obj, full_document=False)
  63. self.assertEqual(xml, '<a>1</a><a>2</a>')
  64. def test_nested(self):
  65. obj = {'a': {'b': '1', 'c': '2'}}
  66. self.assertEqual(obj, parse(unparse(obj)))
  67. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  68. obj = {'a': {'b': {'c': {'@a': 'x', '#text': 'y'}}}}
  69. self.assertEqual(obj, parse(unparse(obj)))
  70. self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
  71. def test_semistructured(self):
  72. xml = '<a>abc<d/>efg</a>'
  73. self.assertEqual(_strip(unparse(parse(xml))),
  74. '<a><d></d>abcefg</a>')
  75. def test_preprocessor(self):
  76. obj = {'a': OrderedDict((('b:int', [1, 2]), ('b', 'c')))}
  77. def p(key, value):
  78. try:
  79. key, _ = key.split(':')
  80. except ValueError:
  81. pass
  82. return key, value
  83. self.assertEqual(_strip(unparse(obj, preprocessor=p)),
  84. '<a><b>1</b><b>2</b><b>c</b></a>')
  85. def test_preprocessor_skipkey(self):
  86. obj = {'a': {'b': 1, 'c': 2}}
  87. def p(key, value):
  88. if key == 'b':
  89. return None
  90. return key, value
  91. self.assertEqual(_strip(unparse(obj, preprocessor=p)),
  92. '<a><c>2</c></a>')
  93. if not IS_JYTHON:
  94. # Jython's SAX does not preserve attribute order
  95. def test_attr_order_roundtrip(self):
  96. xml = '<root a="1" b="2" c="3"></root>'
  97. self.assertEqual(xml, _strip(unparse(parse(xml))))
  98. def test_pretty_print(self):
  99. obj = {'a': OrderedDict((
  100. ('b', [{'c': [1, 2]}, 3]),
  101. ('x', 'y'),
  102. ))}
  103. newl = '\n'
  104. indent = '....'
  105. xml = dedent('''\
  106. <?xml version="1.0" encoding="utf-8"?>
  107. <a>
  108. ....<b>
  109. ........<c>1</c>
  110. ........<c>2</c>
  111. ....</b>
  112. ....<b>3</b>
  113. ....<x>y</x>
  114. </a>''')
  115. self.assertEqual(xml, unparse(obj, pretty=True,
  116. newl=newl, indent=indent))
  117. def test_encoding(self):
  118. try:
  119. value = unichr(39321)
  120. except NameError:
  121. value = chr(39321)
  122. obj = {'a': value}
  123. utf8doc = unparse(obj, encoding='utf-8')
  124. latin1doc = unparse(obj, encoding='iso-8859-1')
  125. self.assertEqual(parse(utf8doc), parse(latin1doc))
  126. self.assertEqual(parse(utf8doc), obj)
  127. def test_fulldoc(self):
  128. xml_declaration_re = re.compile(
  129. '^' + re.escape('<?xml version="1.0" encoding="utf-8"?>'))
  130. self.assertTrue(xml_declaration_re.match(unparse({'a': 1})))
  131. self.assertFalse(
  132. xml_declaration_re.match(unparse({'a': 1}, full_document=False)))
  133. def test_non_string_value(self):
  134. obj = {'a': 1}
  135. self.assertEqual('<a>1</a>', _strip(unparse(obj)))
  136. def test_non_string_attr(self):
  137. obj = {'a': {'@attr': 1}}
  138. self.assertEqual('<a attr="1"></a>', _strip(unparse(obj)))
  139. def test_short_empty_elements(self):
  140. if sys.version_info[0] < 3:
  141. return
  142. obj = {'a': None}
  143. self.assertEqual('<a/>', _strip(unparse(obj, short_empty_elements=True)))
  144. def test_namespace_support(self):
  145. obj = OrderedDict((
  146. ('http://defaultns.com/:root', OrderedDict((
  147. ('@xmlns', OrderedDict((
  148. ('', 'http://defaultns.com/'),
  149. ('a', 'http://a.com/'),
  150. ('b', 'http://b.com/'),
  151. ))),
  152. ('http://defaultns.com/:x', OrderedDict((
  153. ('@http://a.com/:attr', 'val'),
  154. ('#text', '1'),
  155. ))),
  156. ('http://a.com/:y', '2'),
  157. ('http://b.com/:z', '3'),
  158. ))),
  159. ))
  160. ns = {
  161. 'http://defaultns.com/': '',
  162. 'http://a.com/': 'a',
  163. 'http://b.com/': 'b',
  164. }
  165. expected_xml = '''<?xml version="1.0" encoding="utf-8"?>
  166. <root xmlns="http://defaultns.com/" xmlns:a="http://a.com/" \
  167. xmlns:b="http://b.com/"><x a:attr="val">1</x><a:y>2</a:y><b:z>3</b:z></root>'''
  168. xml = unparse(obj, namespaces=ns)
  169. self.assertEqual(xml, expected_xml)
  170. def test_boolean_unparse(self):
  171. expected_xml = '<?xml version="1.0" encoding="utf-8"?>\n<x>true</x>'
  172. xml = unparse(dict(x=True))
  173. self.assertEqual(xml, expected_xml)
  174. expected_xml = '<?xml version="1.0" encoding="utf-8"?>\n<x>false</x>'
  175. xml = unparse(dict(x=False))
  176. self.assertEqual(xml, expected_xml)