Skip to content

Commit

Permalink
❇️ Add list folding state toggle
Browse files Browse the repository at this point in the history
Taken from quandyfactory#64
  • Loading branch information
Ousret committed Aug 13, 2022
1 parent 447d02e commit 3ce3a01
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 20 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Revision History
================

Version 2.1.0
-------------

* Release Date: 2022-08-13
* Changes:
* Handling bool values properly
* Support for disabling default list folding
* Add /docs

Version 2.0.0
-------------

Expand Down
9 changes: 6 additions & 3 deletions dicttoxml2/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


def dicttoxml(obj: Any, root: bool = True, custom_root: str = 'root', ids: bool = False, attr_type: bool = True,
item_func: Callable[[Any], str] = default_item_func, cdata: bool = False) -> bytes:
item_func: Callable[[Any], str] = default_item_func, cdata: bool = False, fold_list: bool = True) -> bytes:
"""Converts a python object into XML.
Arguments:
- root specifies whether the output is wrapped in an XML root element
Expand All @@ -25,16 +25,19 @@ def dicttoxml(obj: Any, root: bool = True, custom_root: str = 'root', ids: bool
Default is 'item'
- cdata specifies whether string values should be wrapped in CDATA sections.
Default is False
- fold_list when using the option fold_list=False the parameter item_func is ignored.
In case of nested lists, all list entries will use the same parent dictionary name as item name.
Default is True
"""
logger.debug('Inside dicttoxml(): type(obj) is: "%s", obj="%s"' % (type(obj).__name__, obj))
output: List[str] = []

if root:
output.append('<?xml version="1.0" encoding="UTF-8" ?>')
output.append(
f"<{custom_root}>{convert(obj, ids, attr_type, item_func, cdata, parent=custom_root)}</{custom_root}>"
f"<{custom_root}>{convert(obj, ids, attr_type, item_func, cdata, parent=custom_root, fold_list=fold_list)}</{custom_root}>"
)
else:
output.append(convert(obj, ids, attr_type, item_func, cdata, parent=''))
output.append(convert(obj, ids, attr_type, item_func, cdata, parent='', fold_list=fold_list))

return ''.join(output).encode('utf-8')
39 changes: 22 additions & 17 deletions dicttoxml2/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
logger = getLogger("dicttoxml")


def convert(obj: Any, ids: bool, attr_type: bool, item_func: Callable[[Any], str], cdata: bool, parent: str = 'root'):
def convert(obj: Any, ids: bool, attr_type: bool, item_func: Callable[[Any], str], cdata: bool, fold_list: bool, parent: str = 'root'):
"""Routes the elements of an object to the right function to convert them
based on their data type"""

Expand All @@ -31,15 +31,15 @@ def convert(obj: Any, ids: bool, attr_type: bool, item_func: Callable[[Any], str
return convert_kv(item_name, obj.isoformat(), attr_type, cdata=cdata)

if isinstance(obj, dict):
return convert_dict(obj, ids, parent, attr_type, item_func, cdata=cdata)
return convert_dict(obj, ids, parent, attr_type, item_func, cdata=cdata, fold_list=fold_list)

if isinstance(obj, Iterable):
return convert_list(obj, ids, parent, attr_type, item_func, cdata=cdata)
return convert_list(obj, ids, parent, attr_type, item_func, cdata=cdata, fold_list=fold_list)

raise TypeError('Unsupported data type: %s (%s)' % (obj, type(obj).__name__))


def convert_dict(obj: Mapping, ids: bool, parent: str, attr_type: bool, item_func: Callable[[Any], str], cdata: bool):
def convert_dict(obj: Mapping, ids: bool, parent: str, attr_type: bool, item_func: Callable[[Any], str], cdata: bool, fold_list: bool):
"""Converts a dict into an XML string."""
logger.debug('Inside convert_dict(): obj type is: "%s", obj="%s"' % (type(obj).__name__, obj))
output = []
Expand All @@ -63,17 +63,22 @@ def convert_dict(obj: Mapping, ids: bool, parent: str, attr_type: bool, item_fun
elif isinstance(val, dict):
if attr_type:
attr['type'] = get_xml_type(val)
output.append('<%s%s>%s</%s>' % (key, make_attrstring(attr), convert_dict(val, ids, key, attr_type, item_func, cdata), key))
output.append('<%s%s>%s</%s>' % (key, make_attrstring(attr), convert_dict(val, ids, key, attr_type, item_func, cdata, fold_list), key))

elif isinstance(val, list):
if attr_type:
attr['type'] = get_xml_type(val)
output.append('<%s%s>%s</%s>' % (
key,
make_attrstring(attr),
convert_list(val, ids, key, attr_type, item_func, cdata),
key
))
if fold_list:
output.append('<%s%s>%s</%s>' % (
key,
make_attrstring(attr),
convert_list(val, ids, key, attr_type, item_func, cdata, fold_list),
key
))
else:
output.append(
convert_list(val, ids, key, attr_type, item_func, cdata, fold_list)
)

elif val is None:
output.append(convert_none(key, val, attr_type, attr, cdata))
Expand All @@ -84,12 +89,12 @@ def convert_dict(obj: Mapping, ids: bool, parent: str, attr_type: bool, item_fun
return ''.join(output)


def convert_list(items: Iterable, ids: bool, parent: str, attr_type: bool, item_func: Callable[[Any], str], cdata: bool):
def convert_list(items: Iterable, ids: bool, parent: str, attr_type: bool, item_func: Callable[[Any], str], cdata: bool, fold_list: bool):
"""Converts a list into an XML string."""
logger.debug('Inside convert_list()')
output = []

item_name = item_func(parent)
item_name = item_func(parent) if fold_list else parent

if ids:
this_id = get_unique_id(parent)
Expand All @@ -109,14 +114,14 @@ def convert_list(items: Iterable, ids: bool, parent: str, attr_type: bool, item_
if not attr_type:
output.append('<%s>%s</%s>' % (
item_name,
convert_dict(item, ids, parent, attr_type, item_func, cdata),
convert_dict(item, ids, parent, attr_type, item_func, cdata, fold_list),
item_name,
)
)
else:
output.append('<%s type="dict">%s</%s>' % (
item_name,
convert_dict(item, ids, parent, attr_type, item_func, cdata),
convert_dict(item, ids, parent, attr_type, item_func, cdata, fold_list),
item_name,
)
)
Expand All @@ -125,14 +130,14 @@ def convert_list(items: Iterable, ids: bool, parent: str, attr_type: bool, item_
if not attr_type:
output.append('<%s %s>%s</%s>' % (
item_name, make_attrstring(attr),
convert_list(item, ids, item_name, attr_type, item_func, cdata),
convert_list(item, ids, item_name, attr_type, item_func, cdata, fold_list),
item_name,
)
)
else:
output.append('<%s type="list"%s>%s</%s>' % (
item_name, make_attrstring(attr),
convert_list(item, ids, item_name, attr_type, item_func, cdata),
convert_list(item, ids, item_name, attr_type, item_func, cdata, fold_list),
item_name,
)
)
Expand Down
28 changes: 28 additions & 0 deletions docs/user/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,31 @@ To turn debug mode off, just call `set_debug` with an argument of `False`:
Debug mode is off.

If you encounter any errors in the code, please file an issue on github: [https://github.com/Ousret/dicttoxml/issues](https://github.com/Ousret/dicttoxml/issues).

List Folding
============

You may want to make list folding act differently. To do so, please set the parameter `fold_list=False`.

Example:


{'book': [{'title': 'Python Programming', 'license': 'GPL', 'author': ['Adam', 'Benny', 'Charlie']}, {'license': 'Apache 2.0', 'title': 'Business Modelling'}]}

Output:

<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
<root>
<book>
<title>Python Programming</title>
<license>GPL</license>
<author>Adam</author>
<author>Benny</author>
<author>Charlie</author>
</book>
<book>
<license>Apache 2.0</license>
<title>Business Modelling</title>
</book>
</root>

6 changes: 6 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,9 @@ def test_primitive_bool_convert() -> None:
data['string'] = 'str_value'

assert dicttoxml(data, root=False, attr_type=False) == b"<true>True</true><false>False</false><int>42</int><float>42.0</float><None></None><string>str_value</string>"


def test_folding_list_ignored() -> None:
payload: dict = {'book': [{'title': 'Python Programming', 'license': 'GPL', 'author': ['Adam', 'Benny', 'Charlie']}, {'license': 'Apache 2.0', 'title': 'Business Modelling'}]}

assert dicttoxml(payload, fold_list=False, attr_type=False) == b'<?xml version="1.0" encoding="UTF-8" ?><root><book><title>Python Programming</title><license>GPL</license><author>Adam</author><author>Benny</author><author>Charlie</author></book><book><license>Apache 2.0</license><title>Business Modelling</title></book></root>'

0 comments on commit 3ce3a01

Please sign in to comment.