The branch, frodo has been updated
via ad42d4c5647dd07974e5d18d31947b06cf739705 (commit)
from 4469de4690cb9e06bcb1cebbb8281259dfdfa127 (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=ad42d4c5647dd07974e5d18d31947b06cf739705
commit ad42d4c5647dd07974e5d18d31947b06cf739705
Author: Martijn Kaijser <[email protected]>
Date: Tue Sep 24 22:13:06 2013 +0200
[script.module.xmltodict] 0.7.0
diff --git a/script.module.xmltodict/README.md
b/script.module.xmltodict/README.md
index 42a6c4b..539e8fc 100644
--- a/script.module.xmltodict/README.md
+++ b/script.module.xmltodict/README.md
@@ -27,11 +27,55 @@ u'complex'
u'element as well'
```
-It's very fast ([Expat](http://docs.python.org/library/pyexpat.html)-based)
and has a streaming mode with a small memory footprint, suitable for big XML
dumps like [Discogs](http://discogs.com/data/) or
[Wikipedia](http://dumps.wikimedia.org/):
+## Namespace support
+
+By default, `xmltodict` does no XML namespace processing (it just treats
namespace declarations as regular node attributes), but passing
`process_namespaces=True` will make it expand namespaces for you:
+
+```python
+>>> xml = """
+... <root xmlns="http://defaultns.com/"
+... xmlns:a="http://a.com/"
+... xmlns:b="http://b.com/">
+... <x>1</x>
+... <a:y>2</a:y>
+... <b:z>3</b:z>
+... </root>
+... """
+>>> assert xmltodict.parse(xml, process_namespaces=True) == {
+... 'http://defaultns.com/:root': {
+... 'http://defaultns.com/:x': '1',
+... 'http://a.com/:y': '2',
+... 'http://b.com/:z': '3',
+... }
+... }
+True
+```
+
+It also lets you collapse certain namespaces to shorthand prefixes, or skip
them altogether:
+
+```python
+>>> namespaces = {
+... 'http://defaultns.com/': None, # skip this namespace
+... 'http://a.com/': 'ns_a', # collapse "http://a.com/" -> "ns_a"
+... }
+>>> assert xmltodict.parse(xml, namespaces=namespaces) == {
+... 'root': {
+... 'x': '1',
+... 'ns_a:y': '2',
+... 'http://b.com/:z': '3',
+... },
+... }
+True
+```
+
+## Streaming mode
+
+`xmltodict` is very fast
([Expat](http://docs.python.org/library/pyexpat.html)-based) and has a
streaming mode with a small memory footprint, suitable for big XML dumps like
[Discogs](http://discogs.com/data/) or [Wikipedia](http://dumps.wikimedia.org/):
```python
>>> def handle_artist(_, artist):
... print artist['name']
+... return True
>>>
>>> xmltodict.parse(GzipFile('discogs_artists.xml.gz'),
... item_depth=2, item_callback=handle_artist)
@@ -77,6 +121,25 @@ $ cat enwiki.dicts.gz | gunzip | script2.py
...
```
+## Roundtripping
+
+You can also convert in the other direction, using the `unparse()` method:
+
+```python
+>>> mydict = {
+... 'page': {
+... 'title': 'King Crimson',
+... 'ns': 0,
+... 'revision': {
+... 'id': 547909091,
+... }
+... }
+... }
+>>> print unparse(mydict)
+<?xml version="1.0" encoding="utf-8"?>
+<page><ns>0</ns><revision><id>547909091</id></revision><title>King
Crimson</title></page>
+```
+
## Ok, how do I get it?
You just need to
@@ -84,3 +147,13 @@ You just need to
```sh
$ pip install xmltodict
```
+
+There is an [official Fedora package for
xmltodict](https://admin.fedoraproject.org/pkgdb/acls/name/python-xmltodict).
If you are on Fedora or RHEL, you can do:
+
+```sh
+$ sudo yum install python-xmltodict
+```
+
+## Donate
+
+If you love `xmltodict`, consider supporting the author [on
Gittip](https://www.gittip.com/martinblech/).
diff --git a/script.module.xmltodict/addon.xml
b/script.module.xmltodict/addon.xml
index bf0b99b..0c2a1fe 100644
--- a/script.module.xmltodict/addon.xml
+++ b/script.module.xmltodict/addon.xml
@@ -1,19 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="script.module.xmltodict"
- name="xmltodict"
- version="0.2.0"
- provider-name="Tristan Fischer ([email protected])">
- <requires>
- <import addon="xbmc.python"
- version="2.0"/>
- </requires>
- <extension point="xbmc.python.module"
- library="lib" />
- <extension point="xbmc.addon.metadata">
- <language></language>
- <summary lang="en">Python helper module</summary>
- <description lang="en">xmltodict is a Python module that makes working
with XML feel like you are working with JSON, as in this "spec".[CR]Original:
https://github.com/martinblech/xmltodict[CR]Author:
[email protected]</description>
- <license>MIT</license>
- <platform>all</platform>
- </extension>
+<addon id="script.module.xmltodict" name="xmltodict" version="0.7.0"
provider-name="Tristan Fischer ([email protected])">
+ <requires>
+ <import addon="xbmc.python" version="2.1.0"/>
+ </requires>
+ <extension point="xbmc.python.module" library="lib"/>
+ <extension point="xbmc.addon.metadata">
+ <language></language>
+ <summary lang="en">Python helper module</summary>
+ <description lang="en">xmltodict is a Python module that makes working
with XML feel like you are working with JSON, as in this "spec".[CR]Original:
https://github.com/martinblech/xmltodict[CR]Author:
[email protected]</description>
+ <license>MIT</license>
+ <platform>all</platform>
+ <website>https://github.com/martinblech/xmltodict</website>
+ <source>https://github.com/martinblech/xmltodict</source>
+ <forum></forum>
+ </extension>
</addon>
\ No newline at end of file
diff --git a/script.module.xmltodict/changelog.txt
b/script.module.xmltodict/changelog.txt
index f61a7ca..6779402 100644
--- a/script.module.xmltodict/changelog.txt
+++ b/script.module.xmltodict/changelog.txt
@@ -1,3 +1,7 @@
+0.7.0 (24.09.2013)
+ Sync with upstream
+ Commit: 594f431bc96bae27cda67deb49aea300111488bd
+
0.2.0
Original: https://github.com/martinblech/xmltodict
Upstream: be842ee121072beb75b643881d8bed5f683cf2c5
\ No newline at end of file
diff --git a/script.module.xmltodict/lib/.travis.yml
b/script.module.xmltodict/lib/.travis.yml
index e1448f1..a4551c5 100644
--- a/script.module.xmltodict/lib/.travis.yml
+++ b/script.module.xmltodict/lib/.travis.yml
@@ -4,6 +4,7 @@ python:
- "2.6"
- "2.7"
- "3.2"
+ - "3.3"
- "pypy"
install:
- pip install .
diff --git a/script.module.xmltodict/lib/xmltodict.py
b/script.module.xmltodict/lib/xmltodict.py
index 9e71c6f..1593072 100644
--- a/script.module.xmltodict/lib/xmltodict.py
+++ b/script.module.xmltodict/lib/xmltodict.py
@@ -14,7 +14,10 @@ except ImportError: # pragma no cover
try: # pragma no cover
from collections import OrderedDict
except ImportError: # pragma no cover
- OrderedDict = dict
+ try:
+ from ordereddict import OrderedDict
+ except ImportError:
+ OrderedDict = dict
try: # pragma no cover
_basestring = basestring
@@ -26,20 +29,25 @@ except NameError: # pragma no cover
_unicode = str
__author__ = 'Martin Blech'
-__version__ = '0.2'
+__version__ = '0.7.0'
__license__ = 'MIT'
class ParsingInterrupted(Exception): pass
-class _DictSAXHandler:
+class _DictSAXHandler(object):
def __init__(self,
- item_depth=0,
- item_callback=lambda *args: True,
- xml_attribs=True,
- attr_prefix='@',
- cdata_key='#text',
- force_cdata=False,
- cdata_separator=''):
+ item_depth=0,
+ item_callback=lambda *args: True,
+ xml_attribs=True,
+ attr_prefix='@',
+ cdata_key='#text',
+ force_cdata=False,
+ cdata_separator='',
+ postprocessor=None,
+ dict_constructor=OrderedDict,
+ strip_whitespace=True,
+ namespace_separator=':',
+ namespaces=None):
self.path = []
self.stack = []
self.data = None
@@ -47,21 +55,46 @@ class _DictSAXHandler:
self.item_depth = item_depth
self.xml_attribs = xml_attribs
self.item_callback = item_callback
- self.attr_prefix = attr_prefix;
+ self.attr_prefix = attr_prefix
self.cdata_key = cdata_key
self.force_cdata = force_cdata
self.cdata_separator = cdata_separator
+ self.postprocessor = postprocessor
+ self.dict_constructor = dict_constructor
+ self.strip_whitespace = strip_whitespace
+ self.namespace_separator = namespace_separator
+ self.namespaces = namespaces
+
+ def _build_name(self, full_name):
+ if not self.namespaces:
+ return full_name
+ i = full_name.rfind(self.namespace_separator)
+ if i == -1:
+ return full_name
+ namespace, name = full_name[:i], full_name[i+1:]
+ short_namespace = self.namespaces.get(namespace, namespace)
+ if not short_namespace:
+ return name
+ else:
+ return self.namespace_separator.join((short_namespace, name))
- def startElement(self, name, attrs):
+ def startElement(self, full_name, attrs):
+ name = self._build_name(full_name)
+ attrs = self.dict_constructor(zip(attrs[0::2], attrs[1::2]))
self.path.append((name, attrs or None))
if len(self.path) > self.item_depth:
self.stack.append((self.item, self.data))
- attrs = OrderedDict((self.attr_prefix+key, value)
+ if self.xml_attribs:
+ attrs = self.dict_constructor(
+ (self.attr_prefix+key, value)
for (key, value) in attrs.items())
- self.item = self.xml_attribs and attrs or None
+ else:
+ attrs = None
+ self.item = attrs or None
self.data = None
-
- def endElement(self, name):
+
+ def endElement(self, full_name):
+ name = self._build_name(full_name)
if len(self.path) == self.item_depth:
item = self.item
if item is None:
@@ -72,38 +105,46 @@ class _DictSAXHandler:
if len(self.stack):
item, data = self.item, self.data
self.item, self.data = self.stack.pop()
+ if self.strip_whitespace and data is not None:
+ data = data.strip() or None
if data and self.force_cdata and item is None:
- item = OrderedDict()
+ item = self.dict_constructor()
if item is not None:
if data:
- item[self.cdata_key] = data
- self.push_data(name, item)
+ self.push_data(item, self.cdata_key, data)
+ self.item = self.push_data(self.item, name, item)
else:
- self.push_data(name, data)
+ self.item = self.push_data(self.item, name, data)
else:
self.item = self.data = None
self.path.pop()
def characters(self, data):
- if data.strip():
- if not self.data:
- self.data = data
- else:
- self.data += self.cdata_separator + data
+ if not self.data:
+ self.data = data
+ else:
+ self.data += self.cdata_separator + data
- def push_data(self, key, data):
- if self.item is None:
- self.item = OrderedDict()
+ def push_data(self, item, key, data):
+ if self.postprocessor is not None:
+ result = self.postprocessor(self.path, key, data)
+ if result is None:
+ return item
+ key, data = result
+ if item is None:
+ item = self.dict_constructor()
try:
- value = self.item[key]
+ value = item[key]
if isinstance(value, list):
value.append(data)
else:
- self.item[key] = [value, data]
+ item[key] = [value, data]
except KeyError:
- self.item[key] = data
+ item[key] = data
+ return item
-def parse(xml_input, *args, **kwargs):
+def parse(xml_input, encoding='utf-8', expat=expat, process_namespaces=False,
+ namespace_separator=':', **kwargs):
"""Parse the given XML input and convert it into a dictionary.
`xml_input` can either be a `string` or a file-like object.
@@ -140,7 +181,7 @@ def parse(xml_input, *args, **kwargs):
>>> def handle(path, item):
... print 'path:%s item:%s' % (path, item)
... return True
- ...
+ ...
>>> xmltodict.parse(\"\"\"
... <a prop="x">
... <b>1</b>
@@ -149,25 +190,60 @@ def parse(xml_input, *args, **kwargs):
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:1
path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:2
+ The optional argument `postprocessor` is a function that takes `path`,
`key`
+ and `value` as positional arguments and returns a new `(key, value)` pair
+ where both `key` and `value` may have changed. Usage example::
+
+ >>> def postprocessor(path, key, value):
+ ... try:
+ ... return key + ':int', int(value)
+ ... except (ValueError, TypeError):
+ ... return key, value
+ >>> xmltodict.parse('<a><b>1</b><b>2</b><b>x</b></a>',
+ ... postprocessor=postprocessor)
+ OrderedDict([(u'a', OrderedDict([(u'b:int', [1, 2]), (u'b', u'x')]))])
+
+ You can pass an alternate version of `expat` (such as `defusedexpat`) by
+ using the `expat` parameter. E.g:
+
+ >>> import defusedexpat
+ >>> xmltodict.parse('<a>hello</a>', expat=defusedexpat.pyexpat)
+ OrderedDict([(u'a', u'hello')])
+
"""
- handler = _DictSAXHandler(*args, **kwargs)
- parser = expat.ParserCreate()
+ handler = _DictSAXHandler(namespace_separator=namespace_separator,
**kwargs)
+ parser = expat.ParserCreate(
+ encoding,
+ namespace_separator if process_namespaces else None
+ )
+ parser.ordered_attributes = True
parser.StartElementHandler = handler.startElement
parser.EndElementHandler = handler.endElement
parser.CharacterDataHandler = handler.characters
- if hasattr(xml_input, 'read'):
+ try:
parser.ParseFile(xml_input)
- else:
+ except (TypeError, AttributeError):
+ if isinstance(xml_input, _unicode):
+ xml_input = xml_input.encode(encoding)
parser.Parse(xml_input, True)
return handler.item
def _emit(key, value, content_handler,
- attr_prefix='@',
- cdata_key='#text',
- root=True):
+ attr_prefix='@',
+ cdata_key='#text',
+ depth=0,
+ preprocessor=None,
+ pretty=False,
+ newl='\n',
+ indent='\t'):
+ if preprocessor is not None:
+ result = preprocessor(key, value)
+ if result is None:
+ return
+ key, value = result
if not isinstance(value, (list, tuple)):
value = [value]
- if root and len(value) > 1:
+ if depth == 0 and len(value) > 1:
raise ValueError('document with multiple roots')
for v in value:
if v is None:
@@ -187,17 +263,35 @@ def _emit(key, value, content_handler,
attrs[ik[len(attr_prefix):]] = iv
continue
children.append((ik, iv))
+ if pretty and depth:
+ content_handler.ignorableWhitespace(newl + indent * depth)
content_handler.startElement(key, AttributesImpl(attrs))
for child_key, child_value in children:
_emit(child_key, child_value, content_handler,
- attr_prefix, cdata_key, False)
+ attr_prefix, cdata_key, depth+1, preprocessor,
+ pretty, newl, indent)
if cdata is not None:
content_handler.characters(cdata)
content_handler.endElement(key)
+ if pretty and depth:
+ content_handler.ignorableWhitespace(newl + indent * (depth - 1))
+def unparse(input_dict, output=None, encoding='utf-8', **kwargs):
+ """Emit an XML document for the given `input_dict` (reverse of `parse`).
-def unparse(item, output=None, encoding='utf-8', **kwargs):
- ((key, value),) = item.items()
+ The resulting XML document is returned as a string, but if `output` (a
+ file-like object) is specified, it is written there instead.
+
+ Dictionary keys prefixed with `attr_prefix` (default=`'@'`) are interpreted
+ as XML node attributes, whereas keys equal to `cdata_key`
+ (default=`'#text'`) are treated as character data.
+
+ The `pretty` parameter (default=`False`) enables pretty-printing. In this
+ mode, lines are terminated with `'\n'` and indented with `'\t'`, but this
+ can be customized with the `newl` and `indent` parameters.
+
+ """
+ ((key, value),) = input_dict.items()
must_return = False
if output == None:
output = StringIO()
@@ -227,8 +321,9 @@ if __name__ == '__main__': # pragma: no cover
try:
root = parse(sys.stdin,
- item_depth=item_depth,
- item_callback=handle_item)
+ item_depth=item_depth,
+ item_callback=handle_item,
+ dict_constructor=dict)
if item_depth == 0:
handle_item([], root)
except KeyboardInterrupt:
diff --git a/script.module.xmltodict/tests/test_dicttoxml.py
b/script.module.xmltodict/tests/test_dicttoxml.py
index 5a8526b..f16398b 100644
--- a/script.module.xmltodict/tests/test_dicttoxml.py
+++ b/script.module.xmltodict/tests/test_dicttoxml.py
@@ -1,10 +1,11 @@
-from xmltodict import parse, unparse
+from xmltodict import parse, unparse, OrderedDict
try:
import unittest2 as unittest
except ImportError:
import unittest
import re
+import collections
_HEADER_RE = re.compile(r'^[^\n]*\n')
def _strip(fullxml):
@@ -42,16 +43,8 @@ class DictToXMLTestCase(unittest.TestCase):
self.assertEqual(unparse(obj), unparse(parse(unparse(obj))))
def test_multiple_roots(self):
- try:
- unparse({'a': '1', 'b': '2'})
- self.fail()
- except ValueError:
- pass
- try:
- unparse({'a': ['1', '2', '3']})
- self.fail()
- except ValueError:
- pass
+ self.assertRaises(ValueError, unparse, {'a':'1', 'b':'2'})
+ self.assertRaises(ValueError, unparse, {'a': ['1', '2', '3']})
def test_nested(self):
obj = {'a': {'b': '1', 'c': '2'}}
@@ -65,3 +58,39 @@ class DictToXMLTestCase(unittest.TestCase):
xml = '<a>abc<d/>efg</a>'
self.assertEqual(_strip(unparse(parse(xml))),
'<a><d></d>abcefg</a>')
+
+ 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)),
+ '<a><b>1</b><b>2</b><b>c</b></a>')
+
+ 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)),
+ '<a><c>2</c></a>')
+
+ if hasattr(collections, 'OrderedDict'):
+ def test_attr_order_roundtrip(self):
+ xml = '<root a="1" b="2" c="3"></root>'
+ self.assertEqual(xml, _strip(unparse(parse(xml))))
+
+ def test_pretty_print(self):
+ obj = {'a': {'b': {'c': 1}}}
+ newl = '_newl_'
+ indent = '_indent_'
+ xml = ('<a>%(newl)s%(indent)s<b>%(newl)s%(indent)s%(indent)s<c>1</c>'
+ '%(newl)s%(indent)s</b>%(newl)s</a>') % {
+ 'newl': newl, 'indent': indent
+ }
+ self.assertEqual(xml, _strip(unparse(obj, pretty=True,
+ newl=newl, indent=indent)))
diff --git a/script.module.xmltodict/tests/test_xmltodict.py
b/script.module.xmltodict/tests/test_xmltodict.py
index 1a30ecd..07af5d0 100644
--- a/script.module.xmltodict/tests/test_xmltodict.py
+++ b/script.module.xmltodict/tests/test_xmltodict.py
@@ -1,4 +1,4 @@
-import xmltodict
+from xmltodict import parse, ParsingInterrupted
try:
import unittest2 as unittest
@@ -7,7 +7,7 @@ except ImportError:
try:
from io import BytesIO as StringIO
except ImportError:
- StringIO = xmltodict.StringIO
+ from xmltodict import StringIO
def _encode(s):
try:
@@ -19,62 +19,87 @@ class XMLToDictTestCase(unittest.TestCase):
def test_string_vs_file(self):
xml = '<a>data</a>'
- self.assertEqual(xmltodict.parse(xml),
- xmltodict.parse(StringIO(_encode(xml))))
+ self.assertEqual(parse(xml),
+ parse(StringIO(_encode(xml))))
def test_minimal(self):
- self.assertEqual(xmltodict.parse('<a/>'),
+ self.assertEqual(parse('<a/>'),
{'a': None})
- self.assertEqual(xmltodict.parse('<a/>', force_cdata=True),
+ self.assertEqual(parse('<a/>', force_cdata=True),
{'a': None})
def test_simple(self):
- self.assertEqual(xmltodict.parse('<a>data</a>'),
+ self.assertEqual(parse('<a>data</a>'),
{'a': 'data'})
def test_force_cdata(self):
- self.assertEqual(xmltodict.parse('<a>data</a>', force_cdata=True),
+ self.assertEqual(parse('<a>data</a>', force_cdata=True),
{'a': {'#text': 'data'}})
def test_custom_cdata(self):
- self.assertEqual(xmltodict.parse('<a>data</a>',
+ self.assertEqual(parse('<a>data</a>',
force_cdata=True,
cdata_key='_CDATA_'),
{'a': {'_CDATA_': 'data'}})
def test_list(self):
- self.assertEqual(xmltodict.parse('<a><b>1</b><b>2</b><b>3</b></a>'),
+ self.assertEqual(parse('<a><b>1</b><b>2</b><b>3</b></a>'),
{'a': {'b': ['1', '2', '3']}})
def test_attrib(self):
- self.assertEqual(xmltodict.parse('<a href="xyz"/>'),
+ self.assertEqual(parse('<a href="xyz"/>'),
{'a': {'@href': 'xyz'}})
def test_skip_attrib(self):
- self.assertEqual(xmltodict.parse('<a href="xyz"/>', xml_attribs=False),
+ self.assertEqual(parse('<a href="xyz"/>', xml_attribs=False),
{'a': None})
def test_custom_attrib(self):
- self.assertEqual(xmltodict.parse('<a href="xyz"/>',
+ self.assertEqual(parse('<a href="xyz"/>',
attr_prefix='!'),
{'a': {'!href': 'xyz'}})
def test_attrib_and_cdata(self):
- self.assertEqual(xmltodict.parse('<a href="xyz">123</a>'),
+ self.assertEqual(parse('<a href="xyz">123</a>'),
{'a': {'@href': 'xyz', '#text': '123'}})
def test_semi_structured(self):
- self.assertEqual(xmltodict.parse('<a>abc<b/>def</a>'),
+ self.assertEqual(parse('<a>abc<b/>def</a>'),
{'a': {'b': None, '#text': 'abcdef'}})
- self.assertEqual(xmltodict.parse('<a>abc<b/>def</a>',
+ self.assertEqual(parse('<a>abc<b/>def</a>',
cdata_separator='\n'),
{'a': {'b': None, '#text': 'abc\ndef'}})
def test_nested_semi_structured(self):
- self.assertEqual(xmltodict.parse('<a>abc<b>123<c/>456</b>def</a>'),
+ self.assertEqual(parse('<a>abc<b>123<c/>456</b>def</a>'),
{'a': {'#text': 'abcdef', 'b': {
'#text': '123456', 'c': None}}})
+ def test_skip_whitespace(self):
+ xml = """
+ <root>
+
+
+ <emptya> </emptya>
+ <emptyb attr="attrvalue">
+
+
+ </emptyb>
+ <value>hello</value>
+ </root>
+ """
+ self.assertEqual(
+ parse(xml),
+ {'root': {'emptya': None,
+ 'emptyb': {'@attr': 'attrvalue'},
+ 'value': 'hello'}})
+
+ def test_keep_whitespace(self):
+ xml = "<root> </root>"
+ self.assertEqual(parse(xml), dict(root=None))
+ self.assertEqual(parse(xml, strip_whitespace=False),
+ dict(root=' '))
+
def test_streaming(self):
def cb(path, item):
cb.count += 1
@@ -82,15 +107,115 @@ class XMLToDictTestCase(unittest.TestCase):
self.assertEqual(item, str(cb.count))
return True
cb.count = 0
- xmltodict.parse('<a x="y"><b>1</b><b>2</b><b>3</b></a>',
- 2, cb)
+ parse('<a x="y"><b>1</b><b>2</b><b>3</b></a>',
+ item_depth=2, item_callback=cb)
self.assertEqual(cb.count, 3)
def test_streaming_interrupt(self):
- def cb(path, item):
- return False
+ cb = lambda path, item: False
+ self.assertRaises(ParsingInterrupted,
+ parse, '<a>x</a>',
+ item_depth=1, item_callback=cb)
+
+ def test_postprocessor(self):
+ def postprocessor(path, key, value):
+ try:
+ return key + ':int', int(value)
+ except (ValueError, TypeError):
+ return key, value
+ self.assertEqual({'a': {'b:int': [1, 2], 'b': 'x'}},
+ parse('<a><b>1</b><b>2</b><b>x</b></a>',
+ postprocessor=postprocessor))
+
+ def test_postprocessor_skip(self):
+ def postprocessor(path, key, value):
+ if key == 'b':
+ value = int(value)
+ if value == 3:
+ return None
+ return key, value
+ self.assertEqual({'a': {'b': [1, 2]}},
+ parse('<a><b>1</b><b>2</b><b>3</b></a>',
+ postprocessor=postprocessor))
+
+ def test_unicode(self):
+ try:
+ value = unichr(39321)
+ except NameError:
+ value = chr(39321)
+ self.assertEqual({'a': value},
+ parse('<a>%s</a>' % value))
+
+ def test_encoded_string(self):
try:
- xmltodict.parse('<a>x</a>', 1, cb)
- self.fail()
- except xmltodict.ParsingInterrupted:
- pass
+ value = unichr(39321)
+ except NameError:
+ value = chr(39321)
+ xml = '<a>%s</a>' % value
+ self.assertEqual(parse(xml),
+ parse(xml.encode('utf-8')))
+
+ def test_namespace_support(self):
+ xml = """
+ <root xmlns="http://defaultns.com/"
+ xmlns:a="http://a.com/"
+ xmlns:b="http://b.com/">
+ <x>1</x>
+ <a:y>2</a:y>
+ <b:z>3</b:z>
+ </root>
+ """
+ d = {
+ 'http://defaultns.com/:root': {
+ 'http://defaultns.com/:x': '1',
+ 'http://a.com/:y': '2',
+ 'http://b.com/:z': '3',
+ }
+ }
+ self.assertEqual(parse(xml, process_namespaces=True), d)
+
+ def test_namespace_collapse(self):
+ xml = """
+ <root xmlns="http://defaultns.com/"
+ xmlns:a="http://a.com/"
+ xmlns:b="http://b.com/">
+ <x>1</x>
+ <a:y>2</a:y>
+ <b:z>3</b:z>
+ </root>
+ """
+ namespaces = {
+ 'http://defaultns.com/': None,
+ 'http://a.com/': 'ns_a',
+ }
+ d = {
+ 'root': {
+ 'x': '1',
+ 'ns_a:y': '2',
+ 'http://b.com/:z': '3',
+ },
+ }
+ self.assertEqual(
+ parse(xml, process_namespaces=True, namespaces=namespaces), d)
+
+ def test_namespace_ignore(self):
+ xml = """
+ <root xmlns="http://defaultns.com/"
+ xmlns:a="http://a.com/"
+ xmlns:b="http://b.com/">
+ <x>1</x>
+ <a:y>2</a:y>
+ <b:z>3</b:z>
+ </root>
+ """
+ d = {
+ 'root': {
+ '@xmlns': 'http://defaultns.com/',
+ '@xmlns:a': 'http://a.com/',
+ '@xmlns:b': 'http://b.com/',
+ 'x': '1',
+ 'a:y': '2',
+ 'b:z': '3',
+ },
+ }
+ self.assertEqual(parse(xml), d)
-----------------------------------------------------------------------
Summary of changes:
script.module.xmltodict/README.md | 75 +++++++++-
script.module.xmltodict/addon.xml | 32 ++--
script.module.xmltodict/changelog.txt | 4 +
script.module.xmltodict/lib/.travis.yml | 1 +
script.module.xmltodict/lib/xmltodict.py | 187 +++++++++++++++++------
script.module.xmltodict/tests/test_dicttoxml.py | 51 +++++--
script.module.xmltodict/tests/test_xmltodict.py | 175 ++++++++++++++++++---
7 files changed, 425 insertions(+), 100 deletions(-)
hooks/post-receive
--
Scripts
------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60133471&iu=/4140/ostg.clktrk
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons