Skip to content
This repository has been archived by the owner on Jan 3, 2018. It is now read-only.

Commit

Permalink
Support for unsorted XML generation and flat lists.
Browse files Browse the repository at this point in the history
If there's a XML schema definition that requires a certain order of elements,
you can now use a collection.OrderedDict together with the keyword parameter sort=False.

Another new feature are flat lists which can be generated by explictly
setting the itemname to None.
  • Loading branch information
Christian Klein committed Jun 18, 2013
1 parent 99f0cd0 commit af745ae
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Changes
=======

* 0.64: huTools.structured.dict2xml now supports flat lists and unordered content
* 0.63: huTools.cache added
* 0.62: huTools.structured.Struct has a __nonzero__ method
* 0.61: huTools.structured.Struct and make_struct handle lists and get an items() method
Expand Down
49 changes: 33 additions & 16 deletions huTools/structured.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import xml.etree.cElementTree as ET
from StringIO import StringIO


# Basic conversation goal here is converting a dict to an object allowing
# more comfortable access. `Struct()` and `make_struct()` are used to archive
# this goal.
Expand Down Expand Up @@ -206,20 +205,31 @@ def make_struct(obj, default=None, nodefault=False):


# Code is based on http://code.activestate.com/recipes/573463/
def _convert_dict_to_xml_recurse(parent, dictitem, listnames):
def _convert_dict_to_xml_recurse(parent, dictitem, listnames, sort=True):
"""Helper Function for XML conversion."""
# we can't convert bare lists
assert not isinstance(dictitem, list)

if isinstance(dictitem, list):
raise TypeError('Unable to convert bare lists')

if isinstance(dictitem, dict):
for (tag, child) in sorted(dictitem.iteritems()):
items = dictitem.iteritems()
if sort:
items = sorted(items)
for (tag, child) in items:
if isinstance(child, list):
# iterate through the array and convert
listelem = ET.Element(tag)
parent.append(listelem)
itemname = listnames.get(tag, 'item')
if itemname is not None:
listelem = ET.SubElement(parent, tag)
else:
listelem = parent

for listchild in child:
elem = ET.Element(listnames.get(tag, 'item'))
listelem.append(elem)
if itemname is not None:
elem = ET.SubElement(listelem, itemname)
else:
elem = ET.SubElement(listelem, tag)

_convert_dict_to_xml_recurse(elem, listchild, listnames)
else:
if tag.startswith('@'):
Expand All @@ -232,7 +242,7 @@ def _convert_dict_to_xml_recurse(parent, dictitem, listnames):
parent.text = unicode(dictitem)


def dict2et(xmldict, roottag='data', listnames=None):
def dict2et(xmldict, roottag='data', listnames=None, sort=True):
"""Converts a dict to an ElementTree.
Converts a dictionary to an XML ElementTree Element::
Expand All @@ -242,9 +252,9 @@ def dict2et(xmldict, roottag='data', listnames=None):
>>> ET.tostring(root)
'<data><nr>xq12</nr><positionen><item><m>12</m></item><item><m>2</m></item></positionen></data>'
Per default ecerything ins put in an enclosing '<data>' element. Also per default lists are converted
to collecitons of `<item>` elements. But by provding a mapping between list names and element names,
you van generate different elements::
Per default everything is put in an enclosing '<data>' element. Also per default lists are converted
to collections of `<item>` elements. By provding a mapping between list names and element names,
you can generate different elements:
>>> data = {"positionen": [{"m": 12}, {"m": 2}]}
>>> root = dict2et(data, roottag='xml')
Expand All @@ -255,6 +265,11 @@ def dict2et(xmldict, roottag='data', listnames=None):
>>> ET.tostring(root)
'<xml><positionen><position><m>12</m></position><position><m>2</m></position></positionen></xml>'
If you explictly set the elementname to None, a flat list is created:
>>> root = dict2et(data, roottag='flat', listnames={'positionen': None})
>>> ET.tostring(root)
'<flat><positionen><m>12</m></positionen><positionen><m>2</m></positionen></flat>'
>>> data = {"kommiauftragsnr":2103839, "anliefertermin":"2009-11-25", "prioritaet": 7,
... "ort": u"Hücksenwagen",
... "positionen": [{"menge": 12, "artnr": "14640/XL", "posnr": 1},],
Expand Down Expand Up @@ -285,12 +300,14 @@ def dict2et(xmldict, roottag='data', listnames=None):
<prioritaet>7</prioritaet>
<kommiauftragsnr>2103839</kommiauftragsnr>
</kommiauftrag>'''
Sorting can be disabled which is only useful for collections.OrderedDict.
"""

if not listnames:
listnames = {}
root = ET.Element(roottag)
_convert_dict_to_xml_recurse(root, xmldict, listnames)
_convert_dict_to_xml_recurse(root, xmldict, listnames, sort=sort)
return root


Expand All @@ -304,12 +321,12 @@ def list2et(xmllist, root, elementname):
return basexml.find(root)


def dict2xml(datadict, roottag='data', listnames=None, pretty=False):
def dict2xml(datadict, roottag='data', listnames=None, pretty=False, sort=True):
"""Converts a dictionary to an UTF-8 encoded XML string.
See also dict2et()
"""
root = dict2et(datadict, roottag, listnames)
root = dict2et(datadict, roottag, listnames, sort=sort)
return to_string(root, pretty=pretty)


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

from distutils.core import setup

version = '0.63'
version = '0.64'

setup(
name='huTools',
Expand Down

0 comments on commit af745ae

Please sign in to comment.