Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-xmltodict for openSUSE:Factory checked in at 2022-06-20 15:37:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-xmltodict (Old) and /work/SRC/openSUSE:Factory/.python-xmltodict.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-xmltodict" Mon Jun 20 15:37:03 2022 rev:7 rq:983728 version:0.13.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-xmltodict/python-xmltodict.changes 2022-02-23 16:25:44.431507307 +0100 +++ /work/SRC/openSUSE:Factory/.python-xmltodict.new.1548/python-xmltodict.changes 2022-06-20 15:37:25.214872203 +0200 @@ -1,0 +2,17 @@ +Sun Jun 19 17:47:26 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.13.0: + * Add install info to readme for openSUSE. (#205) + * Support defaultdict for namespace mapping (#211) + * parse(generator) is now possible (#212) + * Processing comments on parsing from xml to dict (connected to #109) (#221) + * Add expand_iter kw to unparse to expand iterables (#213) + * Fixed some typos + * Add support for python3.8 + * Drop Jython/Python 2 and add Python 3.9/3.10. + * Drop OrderedDict in Python >= 3.7 + * Do not use len() to determine if a sequence is empty + * Add more namespace attribute tests + * Fix encoding issue in setup.py + +------------------------------------------------------------------- Old: ---- xmltodict-0.12.0.tar.gz New: ---- xmltodict-0.13.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-xmltodict.spec ++++++ --- /var/tmp/diff_new_pack.BD12JL/_old 2022-06-20 15:37:25.714872934 +0200 +++ /var/tmp/diff_new_pack.BD12JL/_new 2022-06-20 15:37:25.718872939 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-xmltodict -Version: 0.12.0 +Version: 0.13.0 Release: 0 Summary: Module to make XML working resemble JSON License: MIT ++++++ xmltodict-0.12.0.tar.gz -> xmltodict-0.13.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/PKG-INFO new/xmltodict-0.13.0/PKG-INFO --- old/xmltodict-0.12.0/PKG-INFO 2019-02-11 08:00:08.000000000 +0100 +++ new/xmltodict-0.13.0/PKG-INFO 2022-05-08 08:59:52.086786000 +0200 @@ -1,232 +1,278 @@ Metadata-Version: 2.1 Name: xmltodict -Version: 0.12.0 +Version: 0.13.0 Summary: Makes working with XML feel like you are working with JSON Home-page: https://github.com/martinblech/xmltodict Author: Martin Blech Author-email: martinbl...@gmail.com License: MIT -Description: # xmltodict - - `xmltodict` is a Python module that makes working with XML feel like you are working with [JSON](http://docs.python.org/library/json.html), as in this ["spec"](http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html): - - [![Build Status](https://secure.travis-ci.org/martinblech/xmltodict.svg)](http://travis-ci.org/martinblech/xmltodict) - - ```python - >>> print(json.dumps(xmltodict.parse(""" - ... <mydocument has="an attribute"> - ... <and> - ... <many>elements</many> - ... <many>more elements</many> - ... </and> - ... <plus a="complex"> - ... element as well - ... </plus> - ... </mydocument> - ... """), indent=4)) - { - "mydocument": { - "@has": "an attribute", - "and": { - "many": [ - "elements", - "more elements" - ] - }, - "plus": { - "@a": "complex", - "#text": "element as well" - } - } - } - ``` - - ## 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> - ... """ - >>> 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" - ... } - >>> xmltodict.parse(xml, process_namespaces=True, 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) - A Perfect Circle - Fant??mas - King Crimson - Chris Potter - ... - ``` - - It can also be used from the command line to pipe objects to a script like this: - - ```python - import sys, marshal - while True: - _, article = marshal.load(sys.stdin) - print(article['title']) - ``` - - ```sh - $ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | myscript.py - AccessibleComputing - Anarchism - AfghanistanHistory - AfghanistanGeography - AfghanistanPeople - AfghanistanCommunications - Autism - ... - ``` - - Or just cache the dicts so you don't have to parse that big XML file again. You do this only once: - - ```sh - $ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | gzip > enwiki.dicts.gz - ``` - - And you reuse the dicts with every script that needs them: - - ```sh - $ gunzip enwiki.dicts.gz | script1.py - $ gunzip enwiki.dicts.gz | script2.py - ... - ``` - - ## Roundtripping - - You can also convert in the other direction, using the `unparse()` method: - - ```python - >>> mydict = { - ... 'response': { - ... 'status': 'good', - ... 'last_updated': '2014-02-16T23:10:12Z', - ... } - ... } - >>> print(unparse(mydict, pretty=True)) - <?xml version="1.0" encoding="utf-8"?> - <response> - <status>good</status> - <last_updated>2014-02-16T23:10:12Z</last_updated> - </response> - ``` - - Text values for nodes can be specified with the `cdata_key` key in the python dict, while node properties can be specified with the `attr_prefix` prefixed to the key name in the python dict. The default value for `attr_prefix` is `@` and the default value for `cdata_key` is `#text`. - - ```python - >>> import xmltodict - >>> - >>> mydict = { - ... 'text': { - ... '@color':'red', - ... '@stroke':'2', - ... '#text':'This is a test' - ... } - ... } - >>> print(xmltodict.unparse(mydict, pretty=True)) - <?xml version="1.0" encoding="utf-8"?> - <text stroke="2" color="red">This is a test</text> - ``` - - ## Ok, how do I get it? - - ### Using pypi - - You just need to - - ```sh - $ pip install xmltodict - ``` - - ### RPM-based distro (Fedora, RHEL, ???) - - There is an [official Fedora package for xmltodict](https://apps.fedoraproject.org/packages/python-xmltodict). - - ```sh - $ sudo yum install python-xmltodict - ``` - - ### Arch Linux - - There is an [official Arch Linux package for xmltodict](https://www.archlinux.org/packages/community/any/python-xmltodict/). - - ```sh - $ sudo pacman -S python-xmltodict - ``` - - ### Debian-based distro (Debian, Ubuntu, ???) - - There is an [official Debian package for xmltodict](https://tracker.debian.org/pkg/python-xmltodict). - - ```sh - $ sudo apt install python-xmltodict - ``` - - ### FreeBSD - - There is an [official FreeBSD port for xmltodict](https://svnweb.freebsd.org/ports/head/devel/py-xmltodict/). - - ```sh - $ pkg install py36-xmltodict - ``` - Platform: all Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: Implementation :: Jython +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Text Processing :: Markup :: XML -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Requires-Python: >=3.4 Description-Content-Type: text/markdown +License-File: LICENSE + +# xmltodict + +`xmltodict` is a Python module that makes working with XML feel like you are working with [JSON](http://docs.python.org/library/json.html), as in this ["spec"](http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html): + +[![Build Status](https://travis-ci.com/martinblech/xmltodict.svg?branch=master)](https://travis-ci.com/martinblech/xmltodict) + +```python +>>> print(json.dumps(xmltodict.parse(""" +... <mydocument has="an attribute"> +... <and> +... <many>elements</many> +... <many>more elements</many> +... </and> +... <plus a="complex"> +... element as well +... </plus> +... </mydocument> +... """), indent=4)) +{ + "mydocument": { + "@has": "an attribute", + "and": { + "many": [ + "elements", + "more elements" + ] + }, + "plus": { + "@a": "complex", + "#text": "element as well" + } + } +} +``` + +## 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> +... """ +>>> 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" +... } +>>> xmltodict.parse(xml, process_namespaces=True, 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) +A Perfect Circle +Fant??mas +King Crimson +Chris Potter +... +``` + +It can also be used from the command line to pipe objects to a script like this: + +```python +import sys, marshal +while True: + _, article = marshal.load(sys.stdin) + print(article['title']) +``` + +```sh +$ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | myscript.py +AccessibleComputing +Anarchism +AfghanistanHistory +AfghanistanGeography +AfghanistanPeople +AfghanistanCommunications +Autism +... +``` + +Or just cache the dicts so you don't have to parse that big XML file again. You do this only once: + +```sh +$ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | gzip > enwiki.dicts.gz +``` + +And you reuse the dicts with every script that needs them: + +```sh +$ gunzip enwiki.dicts.gz | script1.py +$ gunzip enwiki.dicts.gz | script2.py +... +``` + +## Roundtripping + +You can also convert in the other direction, using the `unparse()` method: + +```python +>>> mydict = { +... 'response': { +... 'status': 'good', +... 'last_updated': '2014-02-16T23:10:12Z', +... } +... } +>>> print(unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<response> + <status>good</status> + <last_updated>2014-02-16T23:10:12Z</last_updated> +</response> +``` + +Text values for nodes can be specified with the `cdata_key` key in the python dict, while node properties can be specified with the `attr_prefix` prefixed to the key name in the python dict. The default value for `attr_prefix` is `@` and the default value for `cdata_key` is `#text`. + +```python +>>> import xmltodict +>>> +>>> mydict = { +... 'text': { +... '@color':'red', +... '@stroke':'2', +... '#text':'This is a test' +... } +... } +>>> print(xmltodict.unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<text stroke="2" color="red">This is a test</text> +``` + +Lists that are specified under a key in a dictionary use the key as a tag for each item. But if a list does have a parent key, for example if a list exists inside another list, it does not have a tag to use and the items are converted to a string as shown in the example below. To give tags to nested lists, use the `expand_iter` keyword argument to provide a tag as demonstrated below. Note that using `expand_iter` will break roundtripping. + +```python +>>> mydict = { +... "line": { +... "points": [ +... [1, 5], +... [2, 6], +... ] +... } +... } +>>> print(xmltodict.unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points>[1, 5]</points> + <points>[2, 6]</points> +</line> +>>> print(xmltodict.unparse(mydict, pretty=True, expand_iter="coord")) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points> + <coord>1</coord> + <coord>5</coord> + </points> + <points> + <coord>2</coord> + <coord>6</coord> + </points> +</line> +``` + +## Ok, how do I get it? + +### Using pypi + +You just need to + +```sh +$ pip install xmltodict +``` + +### RPM-based distro (Fedora, RHEL, ???) + +There is an [official Fedora package for xmltodict](https://apps.fedoraproject.org/packages/python-xmltodict). + +```sh +$ sudo yum install python-xmltodict +``` + +### Arch Linux + +There is an [official Arch Linux package for xmltodict](https://www.archlinux.org/packages/community/any/python-xmltodict/). + +```sh +$ sudo pacman -S python-xmltodict +``` + +### Debian-based distro (Debian, Ubuntu, ???) + +There is an [official Debian package for xmltodict](https://tracker.debian.org/pkg/python-xmltodict). + +```sh +$ sudo apt install python-xmltodict +``` + +### FreeBSD + +There is an [official FreeBSD port for xmltodict](https://svnweb.freebsd.org/ports/head/devel/py-xmltodict/). + +```sh +$ pkg install py36-xmltodict +``` + +### openSUSE/SLE (SLE 15, Leap 15, Tumbleweed) + +There is an [official openSUSE package for xmltodict](https://software.opensuse.org/package/python-xmltodict). + +```sh +# Python2 +$ zypper in python2-xmltodict + +# Python3 +$ zypper in python3-xmltodict +``` + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/README.md new/xmltodict-0.13.0/README.md --- old/xmltodict-0.12.0/README.md 2019-02-11 07:36:33.000000000 +0100 +++ new/xmltodict-0.13.0/README.md 2019-12-08 10:15:13.000000000 +0100 @@ -2,7 +2,7 @@ `xmltodict` is a Python module that makes working with XML feel like you are working with [JSON](http://docs.python.org/library/json.html), as in this ["spec"](http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html): -[![Build Status](https://secure.travis-ci.org/martinblech/xmltodict.svg)](http://travis-ci.org/martinblech/xmltodict) +[![Build Status](https://travis-ci.com/martinblech/xmltodict.svg?branch=master)](https://travis-ci.com/martinblech/xmltodict) ```python >>> print(json.dumps(xmltodict.parse(""" @@ -163,6 +163,37 @@ <text stroke="2" color="red">This is a test</text> ``` +Lists that are specified under a key in a dictionary use the key as a tag for each item. But if a list does have a parent key, for example if a list exists inside another list, it does not have a tag to use and the items are converted to a string as shown in the example below. To give tags to nested lists, use the `expand_iter` keyword argument to provide a tag as demonstrated below. Note that using `expand_iter` will break roundtripping. + +```python +>>> mydict = { +... "line": { +... "points": [ +... [1, 5], +... [2, 6], +... ] +... } +... } +>>> print(xmltodict.unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points>[1, 5]</points> + <points>[2, 6]</points> +</line> +>>> print(xmltodict.unparse(mydict, pretty=True, expand_iter="coord")) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points> + <coord>1</coord> + <coord>5</coord> + </points> + <points> + <coord>2</coord> + <coord>6</coord> + </points> +</line> +``` + ## Ok, how do I get it? ### Using pypi @@ -204,3 +235,15 @@ ```sh $ pkg install py36-xmltodict ``` + +### openSUSE/SLE (SLE 15, Leap 15, Tumbleweed) + +There is an [official openSUSE package for xmltodict](https://software.opensuse.org/package/python-xmltodict). + +```sh +# Python2 +$ zypper in python2-xmltodict + +# Python3 +$ zypper in python3-xmltodict +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/setup.py new/xmltodict-0.13.0/setup.py --- old/xmltodict-0.12.0/setup.py 2019-02-11 07:36:33.000000000 +0100 +++ new/xmltodict-0.13.0/setup.py 2022-05-08 08:49:17.000000000 +0200 @@ -8,8 +8,8 @@ import xmltodict -with open('README.md') as f: - long_description = f.read() +with open('README.md', 'rb') as f: + long_description = f.read().decode('utf-8') setup(name='xmltodict', @@ -22,23 +22,23 @@ url='https://github.com/martinblech/xmltodict', license=xmltodict.__license__, platforms=['all'], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=3.4', classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: Implementation :: Jython', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Text Processing :: Markup :: XML', ], py_modules=['xmltodict'], - tests_require=['nose>=1.0', 'coverage'], + tests_require=['nose2', 'coverage'], ) Binary files old/xmltodict-0.12.0/tests/__pycache__/test_dicttoxml.cpython-310.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_dicttoxml.cpython-310.pyc differ Binary files old/xmltodict-0.12.0/tests/__pycache__/test_dicttoxml.cpython-37.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_dicttoxml.cpython-37.pyc differ Binary files old/xmltodict-0.12.0/tests/__pycache__/test_dicttoxml.cpython-39.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_dicttoxml.cpython-39.pyc differ Binary files old/xmltodict-0.12.0/tests/__pycache__/test_xmltodict.cpython-310.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_xmltodict.cpython-310.pyc differ Binary files old/xmltodict-0.12.0/tests/__pycache__/test_xmltodict.cpython-37.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_xmltodict.cpython-37.pyc differ Binary files old/xmltodict-0.12.0/tests/__pycache__/test_xmltodict.cpython-39.pyc and new/xmltodict-0.13.0/tests/__pycache__/test_xmltodict.cpython-39.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/tests/test_dicttoxml.py new/xmltodict-0.13.0/tests/test_dicttoxml.py --- old/xmltodict-0.12.0/tests/test_dicttoxml.py 2019-02-11 07:36:33.000000000 +0100 +++ new/xmltodict-0.13.0/tests/test_dicttoxml.py 2019-12-08 10:15:13.000000000 +0100 @@ -46,6 +46,14 @@ 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('''\ + <?xml version="1.0" encoding="utf-8"?> + <a><b><item>1</item><item>2</item></b><b><item>3</item></b></a>''') + self.assertEqual(exp_xml, unparse(obj, expand_iter="item")) + def test_generator(self): obj = {'a': {'b': ['1', '2', '3']}} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/tests/test_xmltodict.py new/xmltodict-0.13.0/tests/test_xmltodict.py --- old/xmltodict-0.12.0/tests/test_xmltodict.py 2019-02-11 07:36:33.000000000 +0100 +++ new/xmltodict-0.13.0/tests/test_xmltodict.py 2022-05-08 08:49:17.000000000 +0200 @@ -1,4 +1,5 @@ from xmltodict import parse, ParsingInterrupted +import collections import unittest try: @@ -119,6 +120,17 @@ parse, '<a>x</a>', item_depth=1, item_callback=cb) + def test_streaming_generator(self): + def cb(path, item): + cb.count += 1 + self.assertEqual(path, [('a', {'x': 'y'}), ('b', None)]) + self.assertEqual(item, str(cb.count)) + return True + cb.count = 0 + parse((n for n in '<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_postprocessor(self): def postprocessor(path, key, value): try: @@ -171,7 +183,8 @@ xml = """ <root xmlns="http://defaultns.com/" xmlns:a="http://a.com/" - xmlns:b="http://b.com/"> + xmlns:b="http://b.com/" + version="1.00"> <x a:attr="val">1</x> <a:y>2</a:y> <b:z>3</b:z> @@ -179,12 +192,13 @@ """ d = { 'http://defaultns.com/:root': { + '@version': '1.00', + '@xmlns': { + '': 'http://defaultns.com/', + 'a': 'http://a.com/', + 'b': 'http://b.com/', + }, 'http://defaultns.com/:x': { - '@xmlns': { - '': 'http://defaultns.com/', - 'a': 'http://a.com/', - 'b': 'http://b.com/', - }, '@http://a.com/:attr': 'val', '#text': '1', }, @@ -199,7 +213,8 @@ xml = """ <root xmlns="http://defaultns.com/" xmlns:a="http://a.com/" - xmlns:b="http://b.com/"> + xmlns:b="http://b.com/" + version="1.00"> <x a:attr="val">1</x> <a:y>2</a:y> <b:z>3</b:z> @@ -211,12 +226,13 @@ } d = { 'root': { + '@version': '1.00', + '@xmlns': { + '': 'http://defaultns.com/', + 'a': 'http://a.com/', + 'b': 'http://b.com/', + }, 'x': { - '@xmlns': { - '': 'http://defaultns.com/', - 'a': 'http://a.com/', - 'b': 'http://b.com/', - }, '@ns_a:attr': 'val', '#text': '1', }, @@ -227,11 +243,43 @@ res = parse(xml, process_namespaces=True, namespaces=namespaces) self.assertEqual(res, d) + def test_namespace_collapse_all(self): + xml = """ + <root xmlns="http://defaultns.com/" + xmlns:a="http://a.com/" + xmlns:b="http://b.com/" + version="1.00"> + <x a:attr="val">1</x> + <a:y>2</a:y> + <b:z>3</b:z> + </root> + """ + namespaces = collections.defaultdict(lambda: None) + d = { + 'root': { + '@version': '1.00', + '@xmlns': { + '': 'http://defaultns.com/', + 'a': 'http://a.com/', + 'b': 'http://b.com/', + }, + 'x': { + '@attr': 'val', + '#text': '1', + }, + 'y': '2', + 'z': '3', + }, + } + res = parse(xml, process_namespaces=True, namespaces=namespaces) + self.assertEqual(res, d) + def test_namespace_ignore(self): xml = """ <root xmlns="http://defaultns.com/" xmlns:a="http://a.com/" - xmlns:b="http://b.com/"> + xmlns:b="http://b.com/" + version="1.00"> <x>1</x> <a:y>2</a:y> <b:z>3</b:z> @@ -242,6 +290,7 @@ '@xmlns': 'http://defaultns.com/', '@xmlns:a': 'http://a.com/', '@xmlns:b': 'http://b.com/', + '@version': '1.00', 'x': '1', 'a:y': '2', 'b:z': '3', @@ -380,3 +429,31 @@ else: self.assertTrue(False) expat.ParserCreate = ParserCreate + + def test_comments(self): + xml = """ + <a> + <b> + <!-- b comment --> + <c> + <!-- c comment --> + 1 + </c> + <d>2</d> + </b> + </a> + """ + expectedResult = { + 'a': { + 'b': { + '#comment': 'b comment', + 'c': { + + '#comment': 'c comment', + '#text': '1', + }, + 'd': '2', + }, + } + } + self.assertEqual(parse(xml, process_comments=True), expectedResult) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/xmltodict.egg-info/PKG-INFO new/xmltodict-0.13.0/xmltodict.egg-info/PKG-INFO --- old/xmltodict-0.12.0/xmltodict.egg-info/PKG-INFO 2019-02-11 08:00:08.000000000 +0100 +++ new/xmltodict-0.13.0/xmltodict.egg-info/PKG-INFO 2022-05-08 08:59:51.000000000 +0200 @@ -1,232 +1,278 @@ Metadata-Version: 2.1 Name: xmltodict -Version: 0.12.0 +Version: 0.13.0 Summary: Makes working with XML feel like you are working with JSON Home-page: https://github.com/martinblech/xmltodict Author: Martin Blech Author-email: martinbl...@gmail.com License: MIT -Description: # xmltodict - - `xmltodict` is a Python module that makes working with XML feel like you are working with [JSON](http://docs.python.org/library/json.html), as in this ["spec"](http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html): - - [![Build Status](https://secure.travis-ci.org/martinblech/xmltodict.svg)](http://travis-ci.org/martinblech/xmltodict) - - ```python - >>> print(json.dumps(xmltodict.parse(""" - ... <mydocument has="an attribute"> - ... <and> - ... <many>elements</many> - ... <many>more elements</many> - ... </and> - ... <plus a="complex"> - ... element as well - ... </plus> - ... </mydocument> - ... """), indent=4)) - { - "mydocument": { - "@has": "an attribute", - "and": { - "many": [ - "elements", - "more elements" - ] - }, - "plus": { - "@a": "complex", - "#text": "element as well" - } - } - } - ``` - - ## 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> - ... """ - >>> 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" - ... } - >>> xmltodict.parse(xml, process_namespaces=True, 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) - A Perfect Circle - Fant??mas - King Crimson - Chris Potter - ... - ``` - - It can also be used from the command line to pipe objects to a script like this: - - ```python - import sys, marshal - while True: - _, article = marshal.load(sys.stdin) - print(article['title']) - ``` - - ```sh - $ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | myscript.py - AccessibleComputing - Anarchism - AfghanistanHistory - AfghanistanGeography - AfghanistanPeople - AfghanistanCommunications - Autism - ... - ``` - - Or just cache the dicts so you don't have to parse that big XML file again. You do this only once: - - ```sh - $ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | gzip > enwiki.dicts.gz - ``` - - And you reuse the dicts with every script that needs them: - - ```sh - $ gunzip enwiki.dicts.gz | script1.py - $ gunzip enwiki.dicts.gz | script2.py - ... - ``` - - ## Roundtripping - - You can also convert in the other direction, using the `unparse()` method: - - ```python - >>> mydict = { - ... 'response': { - ... 'status': 'good', - ... 'last_updated': '2014-02-16T23:10:12Z', - ... } - ... } - >>> print(unparse(mydict, pretty=True)) - <?xml version="1.0" encoding="utf-8"?> - <response> - <status>good</status> - <last_updated>2014-02-16T23:10:12Z</last_updated> - </response> - ``` - - Text values for nodes can be specified with the `cdata_key` key in the python dict, while node properties can be specified with the `attr_prefix` prefixed to the key name in the python dict. The default value for `attr_prefix` is `@` and the default value for `cdata_key` is `#text`. - - ```python - >>> import xmltodict - >>> - >>> mydict = { - ... 'text': { - ... '@color':'red', - ... '@stroke':'2', - ... '#text':'This is a test' - ... } - ... } - >>> print(xmltodict.unparse(mydict, pretty=True)) - <?xml version="1.0" encoding="utf-8"?> - <text stroke="2" color="red">This is a test</text> - ``` - - ## Ok, how do I get it? - - ### Using pypi - - You just need to - - ```sh - $ pip install xmltodict - ``` - - ### RPM-based distro (Fedora, RHEL, ???) - - There is an [official Fedora package for xmltodict](https://apps.fedoraproject.org/packages/python-xmltodict). - - ```sh - $ sudo yum install python-xmltodict - ``` - - ### Arch Linux - - There is an [official Arch Linux package for xmltodict](https://www.archlinux.org/packages/community/any/python-xmltodict/). - - ```sh - $ sudo pacman -S python-xmltodict - ``` - - ### Debian-based distro (Debian, Ubuntu, ???) - - There is an [official Debian package for xmltodict](https://tracker.debian.org/pkg/python-xmltodict). - - ```sh - $ sudo apt install python-xmltodict - ``` - - ### FreeBSD - - There is an [official FreeBSD port for xmltodict](https://svnweb.freebsd.org/ports/head/devel/py-xmltodict/). - - ```sh - $ pkg install py36-xmltodict - ``` - Platform: all Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: Implementation :: Jython +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Text Processing :: Markup :: XML -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Requires-Python: >=3.4 Description-Content-Type: text/markdown +License-File: LICENSE + +# xmltodict + +`xmltodict` is a Python module that makes working with XML feel like you are working with [JSON](http://docs.python.org/library/json.html), as in this ["spec"](http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html): + +[![Build Status](https://travis-ci.com/martinblech/xmltodict.svg?branch=master)](https://travis-ci.com/martinblech/xmltodict) + +```python +>>> print(json.dumps(xmltodict.parse(""" +... <mydocument has="an attribute"> +... <and> +... <many>elements</many> +... <many>more elements</many> +... </and> +... <plus a="complex"> +... element as well +... </plus> +... </mydocument> +... """), indent=4)) +{ + "mydocument": { + "@has": "an attribute", + "and": { + "many": [ + "elements", + "more elements" + ] + }, + "plus": { + "@a": "complex", + "#text": "element as well" + } + } +} +``` + +## 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> +... """ +>>> 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" +... } +>>> xmltodict.parse(xml, process_namespaces=True, 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) +A Perfect Circle +Fant??mas +King Crimson +Chris Potter +... +``` + +It can also be used from the command line to pipe objects to a script like this: + +```python +import sys, marshal +while True: + _, article = marshal.load(sys.stdin) + print(article['title']) +``` + +```sh +$ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | myscript.py +AccessibleComputing +Anarchism +AfghanistanHistory +AfghanistanGeography +AfghanistanPeople +AfghanistanCommunications +Autism +... +``` + +Or just cache the dicts so you don't have to parse that big XML file again. You do this only once: + +```sh +$ bunzip2 enwiki-pages-articles.xml.bz2 | xmltodict.py 2 | gzip > enwiki.dicts.gz +``` + +And you reuse the dicts with every script that needs them: + +```sh +$ gunzip enwiki.dicts.gz | script1.py +$ gunzip enwiki.dicts.gz | script2.py +... +``` + +## Roundtripping + +You can also convert in the other direction, using the `unparse()` method: + +```python +>>> mydict = { +... 'response': { +... 'status': 'good', +... 'last_updated': '2014-02-16T23:10:12Z', +... } +... } +>>> print(unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<response> + <status>good</status> + <last_updated>2014-02-16T23:10:12Z</last_updated> +</response> +``` + +Text values for nodes can be specified with the `cdata_key` key in the python dict, while node properties can be specified with the `attr_prefix` prefixed to the key name in the python dict. The default value for `attr_prefix` is `@` and the default value for `cdata_key` is `#text`. + +```python +>>> import xmltodict +>>> +>>> mydict = { +... 'text': { +... '@color':'red', +... '@stroke':'2', +... '#text':'This is a test' +... } +... } +>>> print(xmltodict.unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<text stroke="2" color="red">This is a test</text> +``` + +Lists that are specified under a key in a dictionary use the key as a tag for each item. But if a list does have a parent key, for example if a list exists inside another list, it does not have a tag to use and the items are converted to a string as shown in the example below. To give tags to nested lists, use the `expand_iter` keyword argument to provide a tag as demonstrated below. Note that using `expand_iter` will break roundtripping. + +```python +>>> mydict = { +... "line": { +... "points": [ +... [1, 5], +... [2, 6], +... ] +... } +... } +>>> print(xmltodict.unparse(mydict, pretty=True)) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points>[1, 5]</points> + <points>[2, 6]</points> +</line> +>>> print(xmltodict.unparse(mydict, pretty=True, expand_iter="coord")) +<?xml version="1.0" encoding="utf-8"?> +<line> + <points> + <coord>1</coord> + <coord>5</coord> + </points> + <points> + <coord>2</coord> + <coord>6</coord> + </points> +</line> +``` + +## Ok, how do I get it? + +### Using pypi + +You just need to + +```sh +$ pip install xmltodict +``` + +### RPM-based distro (Fedora, RHEL, ???) + +There is an [official Fedora package for xmltodict](https://apps.fedoraproject.org/packages/python-xmltodict). + +```sh +$ sudo yum install python-xmltodict +``` + +### Arch Linux + +There is an [official Arch Linux package for xmltodict](https://www.archlinux.org/packages/community/any/python-xmltodict/). + +```sh +$ sudo pacman -S python-xmltodict +``` + +### Debian-based distro (Debian, Ubuntu, ???) + +There is an [official Debian package for xmltodict](https://tracker.debian.org/pkg/python-xmltodict). + +```sh +$ sudo apt install python-xmltodict +``` + +### FreeBSD + +There is an [official FreeBSD port for xmltodict](https://svnweb.freebsd.org/ports/head/devel/py-xmltodict/). + +```sh +$ pkg install py36-xmltodict +``` + +### openSUSE/SLE (SLE 15, Leap 15, Tumbleweed) + +There is an [official openSUSE package for xmltodict](https://software.opensuse.org/package/python-xmltodict). + +```sh +# Python2 +$ zypper in python2-xmltodict + +# Python3 +$ zypper in python3-xmltodict +``` + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/xmltodict.egg-info/SOURCES.txt new/xmltodict-0.13.0/xmltodict.egg-info/SOURCES.txt --- old/xmltodict-0.12.0/xmltodict.egg-info/SOURCES.txt 2019-02-11 08:00:08.000000000 +0100 +++ new/xmltodict-0.13.0/xmltodict.egg-info/SOURCES.txt 2022-05-08 08:59:52.000000000 +0200 @@ -7,6 +7,12 @@ xmltodict.py tests/test_dicttoxml.py tests/test_xmltodict.py +tests/__pycache__/test_dicttoxml.cpython-310.pyc +tests/__pycache__/test_dicttoxml.cpython-37.pyc +tests/__pycache__/test_dicttoxml.cpython-39.pyc +tests/__pycache__/test_xmltodict.cpython-310.pyc +tests/__pycache__/test_xmltodict.cpython-37.pyc +tests/__pycache__/test_xmltodict.cpython-39.pyc xmltodict.egg-info/PKG-INFO xmltodict.egg-info/SOURCES.txt xmltodict.egg-info/dependency_links.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmltodict-0.12.0/xmltodict.py new/xmltodict-0.13.0/xmltodict.py --- old/xmltodict-0.12.0/xmltodict.py 2019-02-11 07:57:59.000000000 +0100 +++ new/xmltodict-0.13.0/xmltodict.py 2022-05-08 08:49:17.000000000 +0200 @@ -15,7 +15,12 @@ except ImportError: from io import StringIO -from collections import OrderedDict +_dict = dict +import platform +if tuple(map(int, platform.python_version_tuple()[:2])) < (3, 7): + from collections import OrderedDict as _dict + +from inspect import isgenerator try: # pragma no cover _basestring = basestring @@ -27,7 +32,7 @@ _unicode = str __author__ = 'Martin Blech' -__version__ = '0.12.0' +__version__ = '0.13.0' __license__ = 'MIT' @@ -45,11 +50,12 @@ force_cdata=False, cdata_separator='', postprocessor=None, - dict_constructor=OrderedDict, + dict_constructor=_dict, strip_whitespace=True, namespace_separator=':', namespaces=None, - force_list=None): + force_list=None, + comment_key='#comment'): self.path = [] self.stack = [] self.data = [] @@ -66,17 +72,21 @@ self.strip_whitespace = strip_whitespace self.namespace_separator = namespace_separator self.namespaces = namespaces - self.namespace_declarations = OrderedDict() + self.namespace_declarations = dict_constructor() self.force_list = force_list + self.comment_key = comment_key def _build_name(self, full_name): - if not self.namespaces: + if self.namespaces is None: 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) + try: + short_namespace = self.namespaces[namespace] + except KeyError: + short_namespace = namespace if not short_namespace: return name else: @@ -95,7 +105,7 @@ attrs = self._attrs_to_dict(attrs) if attrs and self.namespace_declarations: attrs['xmlns'] = self.namespace_declarations - self.namespace_declarations = OrderedDict() + self.namespace_declarations = self.dict_constructor() self.path.append((name, attrs or None)) if len(self.path) > self.item_depth: self.stack.append((self.item, self.data)) @@ -126,7 +136,7 @@ should_continue = self.item_callback(self.path, item) if not should_continue: raise ParsingInterrupted() - if len(self.stack): + if self.stack: data = (None if not self.data else self.cdata_separator.join(self.data)) item = self.item @@ -152,6 +162,11 @@ else: self.data.append(data) + def comments(self, data): + if self.strip_whitespace: + data = data.strip() + self.item = self.push_data(self.item, self.comment_key, data) + def push_data(self, item, key, data): if self.postprocessor is not None: result = self.postprocessor(self.path, key, data) @@ -185,10 +200,10 @@ def parse(xml_input, encoding=None, expat=expat, process_namespaces=False, - namespace_separator=':', disable_entities=True, **kwargs): + namespace_separator=':', disable_entities=True, process_comments=False, **kwargs): """Parse the given XML input and convert it into a dictionary. - `xml_input` can either be a `string` or a file-like object. + `xml_input` can either be a `string`, a file-like object, or a generator of strings. If `xml_attribs` is `True`, element attributes are put in the dictionary among regular child elements, using `@` as a prefix to avoid collisions. If @@ -243,21 +258,21 @@ ... 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')]))]) + {'a': {'b:int': [1, 2], 'b': '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')]) + {'a': 'hello'} You can use the force_list argument to force lists to be created even when there is only a single child of a given level of hierarchy. The force_list argument is a tuple of keys. If the key for a given level of hierarchy is in the force_list argument, that level of hierarchy will have a list as a child (even if there is only one sub-element). - The index_keys operation takes precendence over this. This is applied + The index_keys operation takes precedence over this. This is applied after any user-supplied postprocessor has already run. For example, given this input: @@ -287,6 +302,36 @@ `force_list` can also be a callable that receives `path`, `key` and `value`. This is helpful in cases where the logic that decides whether a list should be forced is more complex. + + + If `process_comment` is `True` then comment will be added with comment_key + (default=`'#comment'`) to then tag which contains comment + + For example, given this input: + <a> + <b> + <!-- b comment --> + <c> + <!-- c comment --> + 1 + </c> + <d>2</d> + </b> + </a> + + If called with process_comment=True, it will produce + this dictionary: + 'a': { + 'b': { + '#comment': 'b comment', + 'c': { + + '#comment': 'c comment', + '#text': '1', + }, + 'd': '2', + }, + } """ handler = _DictSAXHandler(namespace_separator=namespace_separator, **kwargs) @@ -309,6 +354,8 @@ parser.StartElementHandler = handler.startElement parser.EndElementHandler = handler.endElement parser.CharacterDataHandler = handler.characters + if process_comments: + parser.CommentHandler = handler.comments parser.buffer_text = True if disable_entities: try: @@ -323,6 +370,10 @@ parser.ExternalEntityRefHandler = lambda *x: 1 if hasattr(xml_input, 'read'): parser.ParseFile(xml_input) + elif isgenerator(xml_input): + for chunk in xml_input: + parser.Parse(chunk,False) + parser.Parse(b'',True) else: parser.Parse(xml_input, True) return handler.item @@ -353,7 +404,8 @@ indent='\t', namespace_separator=':', namespaces=None, - full_document=True): + full_document=True, + expand_iter=None): key = _process_namespace(key, namespaces, namespace_separator, attr_prefix) if preprocessor is not None: result = preprocessor(key, value) @@ -368,18 +420,21 @@ if full_document and depth == 0 and index > 0: raise ValueError('document with multiple roots') if v is None: - v = OrderedDict() + v = _dict() elif isinstance(v, bool): if v: v = _unicode('true') else: v = _unicode('false') elif not isinstance(v, dict): - v = _unicode(v) + if expand_iter and hasattr(v, '__iter__') and not isinstance(v, _basestring): + v = _dict(((expand_iter, v),)) + else: + v = _unicode(v) if isinstance(v, _basestring): - v = OrderedDict(((cdata_key, v),)) + v = _dict(((cdata_key, v),)) cdata = None - attrs = OrderedDict() + attrs = _dict() children = [] for ik, iv in v.items(): if ik == cdata_key: @@ -407,7 +462,8 @@ _emit(child_key, child_value, content_handler, attr_prefix, cdata_key, depth+1, preprocessor, pretty, newl, indent, namespaces=namespaces, - namespace_separator=namespace_separator) + namespace_separator=namespace_separator, + expand_iter=expand_iter) if cdata is not None: content_handler.characters(cdata) if pretty and children: