import sys
from xmltodict import parse, unparse
from collections import OrderedDict
import unittest
import re
from textwrap import dedent
IS_JYTHON = sys.platform.startswith('java')
_HEADER_RE = re.compile(r'^[^\n]*\n')
def _strip(fullxml):
return _HEADER_RE.sub('', fullxml)
class DictToXMLTestCase(unittest.TestCase):
def test_root(self):
obj = {'a': None}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_simple_cdata(self):
obj = {'a': 'b'}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_cdata(self):
obj = {'a': {'#text': 'y'}}
self.assertEqual(obj, parse(unparse(obj), force_cdata=True))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_attrib(self):
obj = {'a': {'@href': 'x'}}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_attrib_and_cdata(self):
obj = {'a': {'@href': 'x', '#text': 'y'}}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_list(self):
obj = {'a': {'b': ['1', '2', '3']}}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_list_expand_iter(self):
obj = {'a': {'b': [['1', '2'], ['3',]]}}
#self.assertEqual(obj, parse(unparse(obj, expand_iter="item")))
exp_xml = dedent('''\
- 1
- 2
- 3
''')
self.assertEqual(exp_xml, unparse(obj, expand_iter="item"))
def test_generator(self):
obj = {'a': {'b': ['1', '2', '3']}}
def lazy_obj():
return {'a': {'b': (i for i in ('1', '2', '3'))}}
self.assertEqual(obj, parse(unparse(lazy_obj())))
self.assertEqual(unparse(lazy_obj()),
unparse(parse(unparse(lazy_obj()))))
def test_no_root(self):
self.assertRaises(ValueError, unparse, {})
def test_multiple_roots(self):
self.assertRaises(ValueError, unparse, {'a': '1', 'b': '2'})
self.assertRaises(ValueError, unparse, {'a': ['1', '2', '3']})
def test_no_root_nofulldoc(self):
self.assertEqual(unparse({}, full_document=False), '')
def test_multiple_roots_nofulldoc(self):
obj = OrderedDict((('a', 1), ('b', 2)))
xml = unparse(obj, full_document=False)
self.assertEqual(xml, '12')
obj = {'a': [1, 2]}
xml = unparse(obj, full_document=False)
self.assertEqual(xml, '12')
def test_nested(self):
obj = {'a': {'b': '1', 'c': '2'}}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
obj = {'a': {'b': {'c': {'@a': 'x', '#text': 'y'}}}}
self.assertEqual(obj, parse(unparse(obj)))
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_semistructured(self):
xml = 'abcefg'
self.assertEqual(_strip(unparse(parse(xml))),
'abcefg')
def test_preprocessor(self):
obj = {'a': OrderedDict((('b:int', [1, 2]), ('b', 'c')))}
def p(key, value):
try:
key, _ = key.split(':')
except ValueError:
pass
return key, value
self.assertEqual(_strip(unparse(obj, preprocessor=p)),
'12c')
def test_preprocessor_skipkey(self):
obj = {'a': {'b': 1, 'c': 2}}
def p(key, value):
if key == 'b':
return None
return key, value
self.assertEqual(_strip(unparse(obj, preprocessor=p)),
'2')
if not IS_JYTHON:
# Jython's SAX does not preserve attribute order
def test_attr_order_roundtrip(self):
xml = ''
self.assertEqual(xml, _strip(unparse(parse(xml))))
def test_pretty_print(self):
obj = {'a': OrderedDict((
('b', [{'c': [1, 2]}, 3]),
('x', 'y'),
))}
newl = '\n'
indent = '....'
xml = dedent('''\
....
........1
........2
....
....3
....y
''')
self.assertEqual(xml, unparse(obj, pretty=True,
newl=newl, indent=indent))
def test_encoding(self):
try:
value = unichr(39321)
except NameError:
value = chr(39321)
obj = {'a': value}
utf8doc = unparse(obj, encoding='utf-8')
latin1doc = unparse(obj, encoding='iso-8859-1')
self.assertEqual(parse(utf8doc), parse(latin1doc))
self.assertEqual(parse(utf8doc), obj)
def test_fulldoc(self):
xml_declaration_re = re.compile(
'^' + re.escape(''))
self.assertTrue(xml_declaration_re.match(unparse({'a': 1})))
self.assertFalse(
xml_declaration_re.match(unparse({'a': 1}, full_document=False)))
def test_non_string_value(self):
obj = {'a': 1}
self.assertEqual('1', _strip(unparse(obj)))
def test_non_string_attr(self):
obj = {'a': {'@attr': 1}}
self.assertEqual('', _strip(unparse(obj)))
def test_short_empty_elements(self):
if sys.version_info[0] < 3:
return
obj = {'a': None}
self.assertEqual('', _strip(unparse(obj, short_empty_elements=True)))
def test_namespace_support(self):
obj = OrderedDict((
('http://defaultns.com/:root', OrderedDict((
('@xmlns', OrderedDict((
('', 'http://defaultns.com/'),
('a', 'http://a.com/'),
('b', 'http://b.com/'),
))),
('http://defaultns.com/:x', OrderedDict((
('@http://a.com/:attr', 'val'),
('#text', '1'),
))),
('http://a.com/:y', '2'),
('http://b.com/:z', '3'),
))),
))
ns = {
'http://defaultns.com/': '',
'http://a.com/': 'a',
'http://b.com/': 'b',
}
expected_xml = '''
123'''
xml = unparse(obj, namespaces=ns)
self.assertEqual(xml, expected_xml)
def test_boolean_unparse(self):
expected_xml = '\ntrue'
xml = unparse(dict(x=True))
self.assertEqual(xml, expected_xml)
expected_xml = '\nfalse'
xml = unparse(dict(x=False))
self.assertEqual(xml, expected_xml)