From c3f130fcb532ad3eb2971763c23240d18eca00b7 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sat, 30 Apr 2022 18:41:25 +0530 Subject: [PATCH 1/9] feat: add types --- examples/booleanjson.json | 3 +- examples/booleanjson2.json | 5 +- json2xml/dicttoxml.py | 185 +++++++++++++++++++++++++------------ 3 files changed, 129 insertions(+), 64 deletions(-) diff --git a/examples/booleanjson.json b/examples/booleanjson.json index a784c7b7..a00ed5e8 100644 --- a/examples/booleanjson.json +++ b/examples/booleanjson.json @@ -4,5 +4,6 @@ {"boolean_dict": {"boolean": true}}, {"boolean_dict": {"boolean": false}} ], - "boolean_list": [true, false] + "boolean_list": [true, false], + "null_atr": null } diff --git a/examples/booleanjson2.json b/examples/booleanjson2.json index 95a35a8e..3906401a 100644 --- a/examples/booleanjson2.json +++ b/examples/booleanjson2.json @@ -1,5 +1,6 @@ { "boolean_list": [true, false], "number_array": [1, 2, 3], - "string_array": ["a", "b", "c"] -} \ No newline at end of file + "string_array": ["a", "b", "c"], + "null_str": null +} diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 8c57a573..daaf5cdf 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -1,3 +1,5 @@ +from __future__ import annotations + # coding: utf-8 """ @@ -14,7 +16,10 @@ import logging import numbers from random import randint -from typing import Any, Dict +from typing import Any, Dict, Union, Tuple, List, Optional +from typing_extensions import reveal_type +from collections.abc import Callable +from datetime import datetime from defusedxml.minidom import parseString @@ -24,12 +29,12 @@ ids = [] # initialize list of unique ids -def make_id(element, start=100000, end=999999): +def make_id(element: str, start: int = 100000, end: int = 999999) -> str: """Returns a random integer""" return f"{element}_{randint(start, end)}" -def get_unique_id(element): +def get_unique_id(element: str) -> str: """Returns a unique id for a given element""" this_id = make_id(element) dup = True @@ -42,27 +47,33 @@ def get_unique_id(element): return ids[-1] -def get_xml_type(val): +ELEMENT = Union[ + str, int, float, bool, numbers.Number, collections.abc.Iterable, datetime, None +] + + +def get_xml_type(val: ELEMENT) -> str: """Returns the data type for the xml type attribute""" - if type(val).__name__ in ("str", "unicode"): - return "str" - if type(val).__name__ in ("int", "long"): - return "int" - if type(val).__name__ == "float": - return "float" - if type(val).__name__ == "bool": - return "bool" - if isinstance(val, numbers.Number): - return "number" - if type(val).__name__ == "NoneType": + print("get_xml_type", type(val)) + if val is not None: + if type(val).__name__ in ("str", "unicode"): + return "str" + if type(val).__name__ in ("int", "long"): + return "int" + if type(val).__name__ == "float": + return "float" + if type(val).__name__ == "bool": + return "bool" + if isinstance(val, numbers.Number): + return "number" + if isinstance(val, dict): + return "dict" + if isinstance(val, collections.abc.Iterable): + return "list" + else: return "null" - if isinstance(val, dict): - return "dict" - if isinstance(val, collections.abc.Iterable): - return "list" return type(val).__name__ - def escape_xml(s: str) -> str: if isinstance(s, str): s = str(s) # avoid UnicodeDecodeError @@ -74,13 +85,13 @@ def escape_xml(s: str) -> str: return s -def make_attrstring(attr): +def make_attrstring(attr: dict[str, Any]) -> str: """Returns an attribute string in the form key="val" """ attrstring = " ".join([f'{k}="{v}"' for k, v in attr.items()]) return f'{" " if attrstring != "" else ""}{attrstring}' -def key_is_valid_xml(key): +def key_is_valid_xml(key: str) -> bool: """Checks that a key is a valid XML name""" LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') test_xml = f'<{key}>foo' @@ -91,7 +102,7 @@ def key_is_valid_xml(key): return False -def make_valid_xml_name(key, attr: Dict[str, Any]): +def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: """Tests an XML name and fixes it if invalid""" LOG.info( f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' @@ -129,11 +140,19 @@ def wrap_cdata(s: str) -> str: return "" -def default_item_func(parent): +def default_item_func(parent: str) -> str: return "item" -def convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="root"): +def convert( + obj: ELEMENT, + ids: Any, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, + parent: str = "root", +) -> str: """Routes the elements of an object to the right function to convert them based on their data type""" LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') @@ -147,9 +166,14 @@ def convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="root"): # here, we just change order and check for bool first, because no other # type other than bool can be true for bool check if isinstance(obj, bool): - return convert_bool(item_name, obj, attr_type, cdata) + return convert_bool(key=item_name, val=obj, attr_type=attr_type, cdata=cdata) + + if isinstance(obj, numbers.Number): + return convert_kv( + key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata + ) - if isinstance(obj, (numbers.Number, str)): + if isinstance(obj, str): return convert_kv( key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata ) @@ -163,7 +187,10 @@ def convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="root"): cdata=cdata, ) + print("def convert attr_type", type(attr_type)) if obj is None: + import pdb; pdb.set_trace() + print(attr_type) return convert_none(item_name, "", attr_type, cdata) if isinstance(obj, dict): @@ -177,12 +204,16 @@ def convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="root"): def is_primitive_type(val): t = get_xml_type(val) - return t in {'str', 'int', 'float', 'bool', 'number', 'null'} + return t in {"str", "int", "float", "bool", "number", "null"} -def dict2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap, parentIsList): - keys_str = ', '.join(str(key) for key in item) - LOG.info(f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"') +def dict2xml_str( + attr_type, attr, item, item_func, cdata, item_name, item_wrap, parentIsList +): + keys_str = ", ".join(str(key) for key in item) + LOG.info( + f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' + ) # avoid cpu consuming object serialization => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' item="{str(item)}"') @@ -195,7 +226,9 @@ def dict2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap, subtree = rawitem else: # we can not use convert_dict, because rawitem could be non-dict - subtree = convert(rawitem, ids, attr_type, item_func, cdata, item_wrap, item_name) + subtree = convert( + rawitem, ids, attr_type, item_func, cdata, item_wrap, item_name + ) if item.get("@flat", False) or (parentIsList and not item_wrap): return subtree attrstring = make_attrstring(attr) @@ -206,7 +239,7 @@ def list2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap): if attr_type: attr["type"] = get_xml_type(item) flat = False - if item_name.endswith('@flat'): + if item_name.endswith("@flat"): item_name = item_name[0:-5] flat = True subtree = convert_list(item, ids, item_name, attr_type, item_func, cdata, item_wrap) @@ -216,10 +249,12 @@ def list2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap): return f"<{item_name}{attrstring}>{subtree}" -def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap): +def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) -> str: """Converts a dict into an XML string.""" - keys_str = ', '.join(str(key) for key in obj) - LOG.info(f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"') + keys_str = ", ".join(str(key) for key in obj) + LOG.info( + f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' + ) # avoid cpu consuming object serialization => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' obj="{str(obj)}"') @@ -228,7 +263,9 @@ def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap): addline = output.append for key, val in obj.items(): - LOG.info(f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"') + LOG.info( + f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' + ) if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' val="{str(val)}"') @@ -262,10 +299,16 @@ def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap): ) elif isinstance(val, dict): - addline(dict2xml_str(attr_type, attr, val, item_func, cdata, key, item_wrap, False)) + addline( + dict2xml_str( + attr_type, attr, val, item_func, cdata, key, item_wrap, False + ) + ) elif isinstance(val, collections.abc.Iterable): - addline(list2xml_str(attr_type, attr, val, item_func, cdata, key, item_wrap)) + addline( + list2xml_str(attr_type, attr, val, item_func, cdata, key, item_wrap) + ) elif not val: addline(convert_none(key, val, attr_type, attr, cdata)) @@ -276,7 +319,15 @@ def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap): return "".join(output) -def convert_list(items, ids, parent, attr_type, item_func, cdata, item_wrap): +def convert_list( + items: List[Any], + ids: List[int], + parent: str, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, +) -> str: """Converts a list into an XML string.""" LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') # avoid cpu consuming object serialization => extra if @@ -287,14 +338,16 @@ def convert_list(items, ids, parent, attr_type, item_func, cdata, item_wrap): addline = output.append item_name = item_func(parent) - if item_name.endswith('@flat'): + if item_name.endswith("@flat"): item_name = item_name[:-5] this_id = None if ids: this_id = get_unique_id(parent) for i, item in enumerate(items): - LOG.info(f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"') + LOG.info( + f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' + ) # avoid cpu consuming object serialization => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' item="{str(item)}"') @@ -338,10 +391,18 @@ def convert_list(items, ids, parent, attr_type, item_func, cdata, item_wrap): ) elif isinstance(item, dict): - addline(dict2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap, True)) + addline( + dict2xml_str( + attr_type, attr, item, item_func, cdata, item_name, item_wrap, True + ) + ) elif isinstance(item, collections.abc.Iterable): - addline(list2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap)) + addline( + list2xml_str( + attr_type, attr, item, item_func, cdata, item_name, item_wrap + ) + ) elif item is None: addline(convert_none(item_name, None, attr_type, attr, cdata)) @@ -351,12 +412,13 @@ def convert_list(items, ids, parent, attr_type, item_func, cdata, item_wrap): return "".join(output) -def convert_kv(key, val, attr_type, attr={}, cdata: bool = False): +def convert_kv( + key: str, val: Union[str, numbers.Number], attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False +) -> str: """Converts a number or string into an XML element""" LOG.info( f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) - key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -365,12 +427,14 @@ def convert_kv(key, val, attr_type, attr={}, cdata: bool = False): return f"<{key}{attrstring}>{wrap_cdata(val) if cdata else escape_xml(val)}" -def convert_bool(key, val, attr_type, attr={}, cdata=False): +def convert_bool( + key: str, val: bool, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False +) -> str: """Converts a boolean into an XML element""" LOG.info( f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) - + print(type(key), type(val), type(attr_type), type(attr), type(cdata)) key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -379,10 +443,9 @@ def convert_bool(key, val, attr_type, attr={}, cdata=False): return f"<{key}{attrstring}>{str(val).lower()}" -def convert_none(key, val, attr_type, attr={}, cdata=False): +def convert_none(key: str, val: None, attr_type: bool, attr: Dict[str, Any]={}, cdata: bool = False) -> str: """Converts a null value into an XML element""" LOG.info(f'Inside convert_none(): key="{str(key)}"') - key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -392,16 +455,16 @@ def convert_none(key, val, attr_type, attr={}, cdata=False): def dicttoxml( - obj, + obj: Dict[str, Any], root: bool = True, - custom_root="root", - ids=False, - attr_type=True, - item_wrap=True, - item_func=default_item_func, - cdata=False, - xml_namespaces={} -): + custom_root: str = "root", + ids: Optional[List[int]] = None, + attr_type: bool = True, + item_wrap: bool = True, + item_func: Callable[[str], str] = default_item_func, + cdata: bool = False, + xml_namespaces: dict[str, Any] = {}, +) -> bytes: """Converts a python object into XML. Arguments: - root specifies whether the output is wrapped in an XML root element @@ -432,13 +495,13 @@ def dicttoxml( {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} which results in 456 """ - LOG.info(f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}"') + LOG.info(f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}') # avoid cpu consuming object serialization (problem for large objects) => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' obj="{str(obj)}"') output = [] - namespacestr = '' + namespacestr = "" for prefix in xml_namespaces: ns = xml_namespaces[prefix] namespacestr += f' xmlns:{prefix}="{ns}"' From 2b40cf7dd12e7a0230f3fcf68eabb5188a447be6 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sat, 30 Apr 2022 19:27:50 +0530 Subject: [PATCH 2/9] --wip-- [skip ci] --- json2xml/dicttoxml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index daaf5cdf..bbdc1285 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -235,7 +235,7 @@ def dict2xml_str( return f"<{item_name}{attrstring}>{subtree}" -def list2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap): +def list2xml_str(attr_type: List[str], attr: Dict[str, Any], item, item_func: collections.abc.Iterable, cdata: bool, item_name: str, item_wrap: bool) -> str: if attr_type: attr["type"] = get_xml_type(item) flat = False @@ -249,7 +249,7 @@ def list2xml_str(attr_type, attr, item, item_func, cdata, item_name, item_wrap): return f"<{item_name}{attrstring}>{subtree}" -def convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) -> str: +def convert_dict(obj: Dict[str, Any], ids: List[int], parent: str, attr_type: bool, item_func:Callable[[str], str], cdata: bool, item_wrap: bool) -> str: """Converts a dict into an XML string.""" keys_str = ", ".join(str(key) for key in obj) LOG.info( From 1dabc7e6ad366367272467aec40446f629352b58 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 00:39:29 +0530 Subject: [PATCH 3/9] feat: type hint full code --- 1 | 594 +++++++++++++++++++++++++++++++++++++ examples/booleanjson2.json | 3 +- json2xml/dicttoxml.py | 144 ++++++--- 3 files changed, 705 insertions(+), 36 deletions(-) create mode 100644 1 diff --git a/1 b/1 new file mode 100644 index 00000000..c347f49f --- /dev/null +++ b/1 @@ -0,0 +1,594 @@ +from __future__ import annotations + +# coding: utf-8 + +""" +Converts a Python dictionary or other native data type into a valid XML string. +Supports item (`int`, `float`, `long`, `decimal.Decimal`, `bool`, `str`, `unicode`, `datetime`, `none` and other + number-like objects) and collection (`list`, `set`, `tuple` and `dict`, as well as iterable and + dict-like objects) data types, with arbitrary nesting for the collections. + Items with a `datetime` type are converted to ISO format strings. + Items with a `None` type become empty XML elements. +This module works with Python 3.7+ +""" + +import collections +import logging +import numbers +from random import randint +from typing import Any, Dict, Union, Tuple, List, Optional +from typing_extensions import reveal_type +from collections.abc import Callable +import datetime + +from defusedxml.minidom import parseString + +LOG = logging.getLogger("dicttoxml") + + +ids: List[str] = [] # initialize list of unique ids + + +def make_id(element: str, start: int = 100000, end: int = 999999) -> str: + """Returns a random integer""" + return f"{element}_{randint(start, end)}" + + +def get_unique_id(element: str) -> str: + """Returns a unique id for a given element""" + this_id = make_id(element) + dup = True + while dup: + if this_id not in ids: + dup = False + print("get_unique_id", type(this_id)) + ids.append(this_id) + else: + print("get_unique_id", type(this_id)) + this_id = make_id(element) + return ids[-1] + + +ELEMENT = Union[ + str, int, float, bool, numbers.Number, collections.abc.Iterable, datetime.datetime, datetime.date, None +] + + +def get_xml_type(val: ELEMENT) -> str: + """Returns the data type for the xml type attribute""" + print("get_xml_type", type(val)) + if val is not None: + if type(val).__name__ in ("str", "unicode"): + return "str" + if type(val).__name__ in ("int", "long"): + return "int" + if type(val).__name__ == "float": + return "float" + if type(val).__name__ == "bool": + return "bool" + if isinstance(val, numbers.Number): + return "number" + if isinstance(val, dict): + return "dict" + if isinstance(val, collections.abc.Iterable): + return "list" + else: + return "null" + return type(val).__name__ + + +def escape_xml(s: Union[str, numbers.Number]) -> str: + if isinstance(s, str): + s = str(s) # avoid UnicodeDecodeError + s = s.replace("&", "&") + s = s.replace('"', """) + s = s.replace("'", "'") + s = s.replace("<", "<") + s = s.replace(">", ">") + return str(s) + + +def make_attrstring(attr: dict[str, Any]) -> str: + """Returns an attribute string in the form key="val" """ + attrstring = " ".join([f'{k}="{v}"' for k, v in attr.items()]) + return f'{" " if attrstring != "" else ""}{attrstring}' + + +def key_is_valid_xml(key: str) -> bool: + """Checks that a key is a valid XML name""" + LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') + test_xml = f'<{key}>foo' + try: + parseString(test_xml) + return True + except Exception: # minidom does not implement exceptions well + return False + + +def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: + """Tests an XML name and fixes it if invalid""" + LOG.info( + f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' + ) + key = escape_xml(key) + # nothing happens at escape_xml if attr is not a string, we don't + # need to pass it to the method at all. + # attr = escape_xml(attr) + + # pass through if key is already valid + if key_is_valid_xml(key): + return key, attr + + # prepend a lowercase n if the key is numeric + if isinstance(key, int) or key.isdigit(): + return f"n{key}", attr + + # replace spaces with underscores if that fixes the problem + if key_is_valid_xml(key.replace(" ", "_")): + return key.replace(" ", "_"), attr + + # allow namespace prefixes + ignore @flat in key + if key_is_valid_xml(key.replace(":", "").replace("@flat", "")): + return key, attr + + # key is still invalid - move it into a name attribute + attr["name"] = key + key = "key" + return key, attr + + +def wrap_cdata(s: Union[str, numbers.Number]) -> str: + """Wraps a string into CDATA sections""" + s = str(s).replace("]]>", "]]]]>") + return "" + + +def default_item_func(parent: str) -> str: + return "item" + + +def convert( + obj: ELEMENT, + ids: Any, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, + parent: str = "root", +) -> str: + """Routes the elements of an object to the right function to convert them + based on their data type""" + LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') + + item_name = item_func(parent) + # since bool is also a subtype of number.Number and int, the check for bool + # never comes and hence we get wrong value for the xml type bool + # here, we just change order and check for bool first, because no other + # type other than bool can be true for bool check + if isinstance(obj, bool): + return convert_bool(key=item_name, val=obj, attr_type=attr_type, cdata=cdata) + + if isinstance(obj, numbers.Number): + return convert_kv( + key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata + ) + + if isinstance(obj, str): + return convert_kv( + key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata + ) + + if hasattr(obj, "isoformat") and isinstance(obj, (datetime.datetime, datetime.date)): + return convert_kv( + key=item_name, + val=obj.isoformat(), + attr_type=attr_type, + attr={}, + cdata=cdata, + ) + + print("def convert attr_type", type(attr_type)) + if obj is None: + return convert_none(key=item_name, val=None, attr_type=attr_type, cdata=cdata) + + if isinstance(obj, dict): + return convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) + + if isinstance(obj, collections.abc.Iterable): + return convert_list(obj, ids, parent, attr_type, item_func, cdata, item_wrap) + + raise TypeError(f"Unsupported data type: {obj} ({type(obj).__name__})") + + +def is_primitive_type(val: Any) -> bool: + t = get_xml_type(val) + return t in {"str", "int", "float", "bool", "number", "null"} + + +def dict2xml_str( + attr_type: bool, + attr: Dict[str, Any], + item: Dict[str, Any], + item_func: Callable[[str], str], + cdata: bool, + item_name: str, + item_wrap: bool, + parentIsList: bool, +) -> str: + """ + dict2xml_str + """ + print( + "dict2xml_str", + type(attr_type), + type(attr), + type(item), + type(item_func), + type(cdata), + type(item_name), + type(item_wrap), + type(parentIsList), + ) + keys_str = ", ".join(str(key) for key in item) + LOG.info( + f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' item="{str(item)}"') + + if attr_type: + attr["type"] = get_xml_type(item) + attr = item.pop("@attrs", attr) # update attr with custom @attr if exists + rawitem = item["@val"] if "@val" in item else item + if is_primitive_type(rawitem): + subtree = rawitem + else: + # we can not use convert_dict, because rawitem could be non-dict + subtree = convert( + rawitem, ids, attr_type, item_func, cdata, item_wrap, item_name + ) + if item.get("@flat", False) or (parentIsList and not item_wrap): + return subtree + attrstring = make_attrstring(attr) + return f"<{item_name}{attrstring}>{subtree}" + + +def list2xml_str( + attr_type: bool, + attr: Dict[str, Any], + item: collections.abc.Iterable[Any], + item_func: Callable[[str], str], + cdata: bool, + item_name: str, + item_wrap: bool, +) -> str: + if isinstance(item, List) or isinstance(item, Set): + if attr_type: + attr["type"] = get_xml_type(item) + flat = False + if item_name.endswith("@flat"): + item_name = item_name[0:-5] + flat = True + subtree = convert_list( + items=item, + ids=ids, + parent=item_name, + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + item_wrap=item_wrap, + ) + import pdb; pdb.set_trace() + if flat or (len(item) > 0 and is_primitive_type(item[0]) and not item_wrap): + return subtree + attrstring = make_attrstring(attr) + return f"<{item_name}{attrstring}>{subtree}" + + +def convert_dict( + obj: Dict[str, Any], + ids: List[str], + parent: str, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, +) -> str: + """Converts a dict into an XML string.""" + keys_str = ", ".join(str(key) for key in obj) + LOG.info( + f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') + + output: List[str] = [] + addline = output.append + + for key, val in obj.items(): + LOG.info( + f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' + ) + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' val="{str(val)}"') + + attr = {} if not ids else {"id": f"{get_unique_id(parent)}"} + + key, attr = make_valid_xml_name(key, attr) + + # since bool is also a subtype of number.Number and int, the check for bool + # never comes and hence we get wrong value for the xml type bool + # here, we just change order and check for bool first, because no other + # type other than bool can be true for bool check + if isinstance(val, bool): + addline(convert_bool(key, val, attr_type, attr, cdata)) + + elif isinstance(val, (numbers.Number, str)): + addline( + convert_kv( + key=key, val=val, attr_type=attr_type, attr=attr, cdata=cdata + ) + ) + + elif hasattr(val, "isoformat"): # datetime + addline( + convert_kv( + key=key, + val=val.isoformat(), + attr_type=attr_type, + attr=attr, + cdata=cdata, + ) + ) + + elif isinstance(val, dict): + addline( + dict2xml_str( + attr_type, attr, val, item_func, cdata, key, item_wrap, False + ) + ) + + elif isinstance(val, collections.abc.Iterable): + print("DEBUGNEW", type(item_func)) + addline( + list2xml_str( + attr_type=attr_type, + attr=attr, + item=val, + item_func=item_func, + cdata=cdata, + item_name=key, + item_wrap=item_wrap, + ) + ) + + elif not val: + addline(convert_none(key, val, attr_type, attr, cdata)) + + else: + raise TypeError(f"Unsupported data type: {val} ({type(val).__name__})") + + return "".join(output) + + +def convert_list( + items: collections.abc.Iterable[Any], + ids: List[str], + parent: str, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, +) -> str: + """Converts a list into an XML string.""" + LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' items="{str(items)}"') + + output: List[str] = [] + addline = output.append + + item_name = item_func(parent) + if item_name.endswith("@flat"): + item_name = item_name[:-5] + this_id = None + if ids: + this_id = get_unique_id(parent) + + for i, item in enumerate(items): + LOG.info( + f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' + ) + # avoid cpu consuming object serialization => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' item="{str(item)}"') + + attr = {} if not ids else {"id": f"{this_id}_{i + 1}"} + + print("DEBUG list_str", type(item_func)) + + if isinstance(item, bool): + addline(convert_bool(item_name, item, attr_type, attr, cdata)) + + elif isinstance(item, (numbers.Number, str)): + if item_wrap: + addline( + convert_kv( + key=item_name, + val=item, + attr_type=attr_type, + attr=attr, + cdata=cdata, + ) + ) + else: + addline( + convert_kv( + key=parent, + val=item, + attr_type=attr_type, + attr=attr, + cdata=cdata, + ) + ) + + elif hasattr(item, "isoformat"): # datetime + addline( + convert_kv( + key=item_name, + val=item.isoformat(), + attr_type=attr_type, + attr=attr, + cdata=cdata, + ) + ) + + elif isinstance(item, dict): + addline( + dict2xml_str( + attr_type, attr, item, item_func, cdata, item_name, item_wrap, True + ) + ) + + elif isinstance(item, collections.abc.Iterable): + print("DEBUGNEW", type(item_func)) + addline( + list2xml_str( + attr_type=attr_type, + attr=attr, + item=item, + item_func=item_func, + cdata=cdata, + item_name=item_name, + item_wrap=item_wrap, + ) + ) + + elif item is None: + addline(convert_none(item_name, None, attr_type, attr, cdata)) + + else: + raise TypeError(f"Unsupported data type: {item} ({type(item).__name__})") + return "".join(output) + + +def convert_kv( + key: str, + val: Union[str, numbers.Number], + attr_type: bool, + attr: Dict[str, Any] = {}, + cdata: bool = False, +) -> str: + """Converts a number or string into an XML element""" + LOG.info( + f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' + ) + print("convert_kv", type(val)) + key, attr = make_valid_xml_name(key, attr) + + if attr_type: + attr["type"] = get_xml_type(val) + attrstring = make_attrstring(attr) + return f"<{key}{attrstring}>{wrap_cdata(val) if cdata else escape_xml(val)}" + + +def convert_bool( + key: str, val: bool, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False +) -> str: + """Converts a boolean into an XML element""" + LOG.info( + f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' + ) + print(type(key), type(val), type(attr_type), type(attr), type(cdata)) + key, attr = make_valid_xml_name(key, attr) + + if attr_type: + attr["type"] = get_xml_type(val) + attrstring = make_attrstring(attr) + return f"<{key}{attrstring}>{str(val).lower()}" + + +def convert_none( + key: str, val: None, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False +) -> str: + """Converts a null value into an XML element""" + LOG.info(f'Inside convert_none(): key="{str(key)}"') + key, attr = make_valid_xml_name(key, attr) + + if attr_type: + attr["type"] = get_xml_type(val) + attrstring = make_attrstring(attr) + return f"<{key}{attrstring}>" + + +def dicttoxml( + obj: Dict[str, Any], + root: bool = True, + custom_root: str = "root", + ids: Optional[List[int]] = None, + attr_type: bool = True, + item_wrap: bool = True, + item_func: Callable[[str], str] = default_item_func, + cdata: bool = False, + xml_namespaces: dict[str, Any] = {}, +) -> bytes: + """Converts a python object into XML. + Arguments: + - root specifies whether the output is wrapped in an XML root element + Default is True + - custom_root allows you to specify a custom root element. + Default is 'root' + - ids specifies whether elements get unique ids. + Default is False + - attr_type specifies whether elements get a data type attribute. + Default is True + - item_func specifies what function should generate the element name for + items in a list. + Default is 'item' + - item_wrap specifies whether to nest items in array in + Default is True + - cdata specifies whether string values should be wrapped in CDATA sections. + Default is False + - xml_namespaces is a dictionary where key is xmlns prefix and value the urn, + e.g. { 'flex': 'http://www.w3.org/flex/flexBase', 'xsl': "http://www.w3.org/1999/XSL/Transform"} + results in + Default is {} + + Dictionaries-keys with special char '@' has special meaning: + @attrs: This allows custom xml attributes: {'@attr':{'a':'b'}, 'x':'y'} results in y + @flat: If a key ends with @flat (or dict contains key '@flat'), encapsulating node is omitted. Similar to item_wrap. + @val: @attrs requires complex dict type. If primitive type should be used, then @val is used as key. + To add custom xml-attributes on a list {'list': [4, 5, 6]}, you do this: + {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} + which results in 456 + """ + LOG.info( + f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' + ) + # avoid cpu consuming object serialization (problem for large objects) => extra if + if LOG.getEffectiveLevel() <= logging.DEBUG: + LOG.debug(f' obj="{str(obj)}"') + + output = [] + namespacestr = "" + for prefix in xml_namespaces: + ns = xml_namespaces[prefix] + namespacestr += f' xmlns:{prefix}="{ns}"' + if root: + output.append('') + output_elem = convert( + obj, ids, attr_type, item_func, cdata, item_wrap, parent=custom_root + ) + output.append(f"<{custom_root}{namespacestr}>{output_elem}") + else: + output.append( + convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="") + ) + + return "".join(output).encode("utf-8") diff --git a/examples/booleanjson2.json b/examples/booleanjson2.json index 3906401a..2cab73b9 100644 --- a/examples/booleanjson2.json +++ b/examples/booleanjson2.json @@ -2,5 +2,6 @@ "boolean_list": [true, false], "number_array": [1, 2, 3], "string_array": ["a", "b", "c"], - "null_str": null + "null_str": null, + "time": "2010-04-20T20:08:21.634121" } diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index bbdc1285..9eb65cab 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -15,18 +15,19 @@ import collections import logging import numbers +import types from random import randint -from typing import Any, Dict, Union, Tuple, List, Optional +from typing import Any, Dict, Union, Tuple, List, Optional, Set from typing_extensions import reveal_type from collections.abc import Callable -from datetime import datetime +import datetime from defusedxml.minidom import parseString LOG = logging.getLogger("dicttoxml") -ids = [] # initialize list of unique ids +ids: List[str] = [] # initialize list of unique ids def make_id(element: str, start: int = 100000, end: int = 999999) -> str: @@ -41,14 +42,16 @@ def get_unique_id(element: str) -> str: while dup: if this_id not in ids: dup = False + print("get_unique_id", type(this_id)) ids.append(this_id) else: + print("get_unique_id", type(this_id)) this_id = make_id(element) return ids[-1] ELEMENT = Union[ - str, int, float, bool, numbers.Number, collections.abc.Iterable, datetime, None + str, int, float, bool, numbers.Number, collections.abc.Sequence, datetime.datetime, datetime.date, None, Dict[str, Any] ] @@ -68,13 +71,14 @@ def get_xml_type(val: ELEMENT) -> str: return "number" if isinstance(val, dict): return "dict" - if isinstance(val, collections.abc.Iterable): + if isinstance(val, collections.abc.Sequence): return "list" else: return "null" return type(val).__name__ -def escape_xml(s: str) -> str: + +def escape_xml(s: Union[str, numbers.Number]) -> str: if isinstance(s, str): s = str(s) # avoid UnicodeDecodeError s = s.replace("&", "&") @@ -82,7 +86,7 @@ def escape_xml(s: str) -> str: s = s.replace("'", "'") s = s.replace("<", "<") s = s.replace(">", ">") - return s + return str(s) def make_attrstring(attr: dict[str, Any]) -> str: @@ -134,7 +138,7 @@ def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, return key, attr -def wrap_cdata(s: str) -> str: +def wrap_cdata(s: Union[str, numbers.Number]) -> str: """Wraps a string into CDATA sections""" s = str(s).replace("]]>", "]]]]>") return "" @@ -178,7 +182,7 @@ def convert( key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata ) - if hasattr(obj, "isoformat"): + if hasattr(obj, "isoformat") and isinstance(obj, (datetime.datetime, datetime.date)): return convert_kv( key=item_name, val=obj.isoformat(), @@ -189,27 +193,46 @@ def convert( print("def convert attr_type", type(attr_type)) if obj is None: - import pdb; pdb.set_trace() - print(attr_type) - return convert_none(item_name, "", attr_type, cdata) + return convert_none(key=item_name, attr_type=attr_type, cdata=cdata) if isinstance(obj, dict): return convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) - if isinstance(obj, collections.abc.Iterable): + if isinstance(obj, collections.abc.Sequence): return convert_list(obj, ids, parent, attr_type, item_func, cdata, item_wrap) raise TypeError(f"Unsupported data type: {obj} ({type(obj).__name__})") -def is_primitive_type(val): +def is_primitive_type(val: Any) -> bool: t = get_xml_type(val) return t in {"str", "int", "float", "bool", "number", "null"} def dict2xml_str( - attr_type, attr, item, item_func, cdata, item_name, item_wrap, parentIsList -): + attr_type: bool, + attr: Dict[str, Any], + item: Dict[str, Any], + item_func: Callable[[str], str], + cdata: bool, + item_name: str, + item_wrap: bool, + parentIsList: bool, +) -> str: + """ + dict2xml_str + """ + print( + "dict2xml_str", + type(attr_type), + type(attr), + type(item), + type(item_func), + type(cdata), + type(item_name), + type(item_wrap), + type(parentIsList), + ) keys_str = ", ".join(str(key) for key in item) LOG.info( f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' @@ -235,21 +258,45 @@ def dict2xml_str( return f"<{item_name}{attrstring}>{subtree}" -def list2xml_str(attr_type: List[str], attr: Dict[str, Any], item, item_func: collections.abc.Iterable, cdata: bool, item_name: str, item_wrap: bool) -> str: +def list2xml_str( + attr_type: bool, + attr: Dict[str, Any], + item: collections.abc.Sequence[Any], + item_func: Callable[[str], str], + cdata: bool, + item_name: str, + item_wrap: bool, +) -> str: if attr_type: attr["type"] = get_xml_type(item) flat = False if item_name.endswith("@flat"): item_name = item_name[0:-5] flat = True - subtree = convert_list(item, ids, item_name, attr_type, item_func, cdata, item_wrap) + subtree = convert_list( + items=item, + ids=ids, + parent=item_name, + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + item_wrap=item_wrap, + ) if flat or (len(item) > 0 and is_primitive_type(item[0]) and not item_wrap): return subtree attrstring = make_attrstring(attr) return f"<{item_name}{attrstring}>{subtree}" -def convert_dict(obj: Dict[str, Any], ids: List[int], parent: str, attr_type: bool, item_func:Callable[[str], str], cdata: bool, item_wrap: bool) -> str: +def convert_dict( + obj: Dict[str, Any], + ids: List[str], + parent: str, + attr_type: bool, + item_func: Callable[[str], str], + cdata: bool, + item_wrap: bool, +) -> str: """Converts a dict into an XML string.""" keys_str = ", ".join(str(key) for key in obj) LOG.info( @@ -259,7 +306,7 @@ def convert_dict(obj: Dict[str, Any], ids: List[int], parent: str, attr_type: bo if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' obj="{str(obj)}"') - output = [] + output: List[str] = [] addline = output.append for key, val in obj.items(): @@ -305,13 +352,22 @@ def convert_dict(obj: Dict[str, Any], ids: List[int], parent: str, attr_type: bo ) ) - elif isinstance(val, collections.abc.Iterable): + elif isinstance(val, collections.abc.Sequence): + print("DEBUGNEW", type(item_func)) addline( - list2xml_str(attr_type, attr, val, item_func, cdata, key, item_wrap) + list2xml_str( + attr_type=attr_type, + attr=attr, + item=val, + item_func=item_func, + cdata=cdata, + item_name=key, + item_wrap=item_wrap, + ) ) elif not val: - addline(convert_none(key, val, attr_type, attr, cdata)) + addline(convert_none(key, attr_type, attr, cdata)) else: raise TypeError(f"Unsupported data type: {val} ({type(val).__name__})") @@ -320,8 +376,8 @@ def convert_dict(obj: Dict[str, Any], ids: List[int], parent: str, attr_type: bo def convert_list( - items: List[Any], - ids: List[int], + items: collections.abc.Sequence[Any], + ids: List[str], parent: str, attr_type: bool, item_func: Callable[[str], str], @@ -334,7 +390,7 @@ def convert_list( if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' items="{str(items)}"') - output = [] + output: List[str] = [] addline = output.append item_name = item_func(parent) @@ -354,6 +410,8 @@ def convert_list( attr = {} if not ids else {"id": f"{this_id}_{i + 1}"} + print("DEBUG list_str", type(item_func)) + if isinstance(item, bool): addline(convert_bool(item_name, item, attr_type, attr, cdata)) @@ -397,15 +455,22 @@ def convert_list( ) ) - elif isinstance(item, collections.abc.Iterable): + elif isinstance(item, collections.abc.Sequence): + print("DEBUGNEW", type(item_func)) addline( list2xml_str( - attr_type, attr, item, item_func, cdata, item_name, item_wrap + attr_type=attr_type, + attr=attr, + item=item, + item_func=item_func, + cdata=cdata, + item_name=item_name, + item_wrap=item_wrap, ) ) elif item is None: - addline(convert_none(item_name, None, attr_type, attr, cdata)) + addline(convert_none(item_name, attr_type, attr, cdata)) else: raise TypeError(f"Unsupported data type: {item} ({type(item).__name__})") @@ -413,12 +478,17 @@ def convert_list( def convert_kv( - key: str, val: Union[str, numbers.Number], attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False + key: str, + val: Union[str, numbers.Number], + attr_type: bool, + attr: Dict[str, Any] = {}, + cdata: bool = False, ) -> str: """Converts a number or string into an XML element""" LOG.info( f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) + print("convert_kv", type(val)) key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -443,13 +513,15 @@ def convert_bool( return f"<{key}{attrstring}>{str(val).lower()}" -def convert_none(key: str, val: None, attr_type: bool, attr: Dict[str, Any]={}, cdata: bool = False) -> str: +def convert_none( + key: str,attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False +) -> str: """Converts a null value into an XML element""" - LOG.info(f'Inside convert_none(): key="{str(key)}"') + # LOG.info(f'Inside convert_none(): key="{str(key)}" val={type(val)}') key, attr = make_valid_xml_name(key, attr) if attr_type: - attr["type"] = get_xml_type(val) + attr["type"] = get_xml_type(None) attrstring = make_attrstring(attr) return f"<{key}{attrstring}>" @@ -458,7 +530,7 @@ def dicttoxml( obj: Dict[str, Any], root: bool = True, custom_root: str = "root", - ids: Optional[List[int]] = None, + ids: Optional[List[int]] = None, attr_type: bool = True, item_wrap: bool = True, item_func: Callable[[str], str] = default_item_func, @@ -495,7 +567,9 @@ def dicttoxml( {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} which results in 456 """ - LOG.info(f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}') + LOG.info( + f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' + ) # avoid cpu consuming object serialization (problem for large objects) => extra if if LOG.getEffectiveLevel() <= logging.DEBUG: LOG.debug(f' obj="{str(obj)}"') From 0b4cd29db5b55f55d9382841cfef5b73cd797d43 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 00:39:38 +0530 Subject: [PATCH 4/9] cleanup --- 1 | 594 -------------------------------------------------------------- 1 file changed, 594 deletions(-) delete mode 100644 1 diff --git a/1 b/1 deleted file mode 100644 index c347f49f..00000000 --- a/1 +++ /dev/null @@ -1,594 +0,0 @@ -from __future__ import annotations - -# coding: utf-8 - -""" -Converts a Python dictionary or other native data type into a valid XML string. -Supports item (`int`, `float`, `long`, `decimal.Decimal`, `bool`, `str`, `unicode`, `datetime`, `none` and other - number-like objects) and collection (`list`, `set`, `tuple` and `dict`, as well as iterable and - dict-like objects) data types, with arbitrary nesting for the collections. - Items with a `datetime` type are converted to ISO format strings. - Items with a `None` type become empty XML elements. -This module works with Python 3.7+ -""" - -import collections -import logging -import numbers -from random import randint -from typing import Any, Dict, Union, Tuple, List, Optional -from typing_extensions import reveal_type -from collections.abc import Callable -import datetime - -from defusedxml.minidom import parseString - -LOG = logging.getLogger("dicttoxml") - - -ids: List[str] = [] # initialize list of unique ids - - -def make_id(element: str, start: int = 100000, end: int = 999999) -> str: - """Returns a random integer""" - return f"{element}_{randint(start, end)}" - - -def get_unique_id(element: str) -> str: - """Returns a unique id for a given element""" - this_id = make_id(element) - dup = True - while dup: - if this_id not in ids: - dup = False - print("get_unique_id", type(this_id)) - ids.append(this_id) - else: - print("get_unique_id", type(this_id)) - this_id = make_id(element) - return ids[-1] - - -ELEMENT = Union[ - str, int, float, bool, numbers.Number, collections.abc.Iterable, datetime.datetime, datetime.date, None -] - - -def get_xml_type(val: ELEMENT) -> str: - """Returns the data type for the xml type attribute""" - print("get_xml_type", type(val)) - if val is not None: - if type(val).__name__ in ("str", "unicode"): - return "str" - if type(val).__name__ in ("int", "long"): - return "int" - if type(val).__name__ == "float": - return "float" - if type(val).__name__ == "bool": - return "bool" - if isinstance(val, numbers.Number): - return "number" - if isinstance(val, dict): - return "dict" - if isinstance(val, collections.abc.Iterable): - return "list" - else: - return "null" - return type(val).__name__ - - -def escape_xml(s: Union[str, numbers.Number]) -> str: - if isinstance(s, str): - s = str(s) # avoid UnicodeDecodeError - s = s.replace("&", "&") - s = s.replace('"', """) - s = s.replace("'", "'") - s = s.replace("<", "<") - s = s.replace(">", ">") - return str(s) - - -def make_attrstring(attr: dict[str, Any]) -> str: - """Returns an attribute string in the form key="val" """ - attrstring = " ".join([f'{k}="{v}"' for k, v in attr.items()]) - return f'{" " if attrstring != "" else ""}{attrstring}' - - -def key_is_valid_xml(key: str) -> bool: - """Checks that a key is a valid XML name""" - LOG.info(f'Inside key_is_valid_xml(). Testing "{str(key)}"') - test_xml = f'<{key}>foo' - try: - parseString(test_xml) - return True - except Exception: # minidom does not implement exceptions well - return False - - -def make_valid_xml_name(key: str, attr: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: - """Tests an XML name and fixes it if invalid""" - LOG.info( - f'Inside make_valid_xml_name(). Testing key "{str(key)}" with attr "{str(attr)}"' - ) - key = escape_xml(key) - # nothing happens at escape_xml if attr is not a string, we don't - # need to pass it to the method at all. - # attr = escape_xml(attr) - - # pass through if key is already valid - if key_is_valid_xml(key): - return key, attr - - # prepend a lowercase n if the key is numeric - if isinstance(key, int) or key.isdigit(): - return f"n{key}", attr - - # replace spaces with underscores if that fixes the problem - if key_is_valid_xml(key.replace(" ", "_")): - return key.replace(" ", "_"), attr - - # allow namespace prefixes + ignore @flat in key - if key_is_valid_xml(key.replace(":", "").replace("@flat", "")): - return key, attr - - # key is still invalid - move it into a name attribute - attr["name"] = key - key = "key" - return key, attr - - -def wrap_cdata(s: Union[str, numbers.Number]) -> str: - """Wraps a string into CDATA sections""" - s = str(s).replace("]]>", "]]]]>") - return "" - - -def default_item_func(parent: str) -> str: - return "item" - - -def convert( - obj: ELEMENT, - ids: Any, - attr_type: bool, - item_func: Callable[[str], str], - cdata: bool, - item_wrap: bool, - parent: str = "root", -) -> str: - """Routes the elements of an object to the right function to convert them - based on their data type""" - LOG.info(f'Inside convert(). type(obj)="{type(obj).__name__}"') - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') - - item_name = item_func(parent) - # since bool is also a subtype of number.Number and int, the check for bool - # never comes and hence we get wrong value for the xml type bool - # here, we just change order and check for bool first, because no other - # type other than bool can be true for bool check - if isinstance(obj, bool): - return convert_bool(key=item_name, val=obj, attr_type=attr_type, cdata=cdata) - - if isinstance(obj, numbers.Number): - return convert_kv( - key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata - ) - - if isinstance(obj, str): - return convert_kv( - key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata - ) - - if hasattr(obj, "isoformat") and isinstance(obj, (datetime.datetime, datetime.date)): - return convert_kv( - key=item_name, - val=obj.isoformat(), - attr_type=attr_type, - attr={}, - cdata=cdata, - ) - - print("def convert attr_type", type(attr_type)) - if obj is None: - return convert_none(key=item_name, val=None, attr_type=attr_type, cdata=cdata) - - if isinstance(obj, dict): - return convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) - - if isinstance(obj, collections.abc.Iterable): - return convert_list(obj, ids, parent, attr_type, item_func, cdata, item_wrap) - - raise TypeError(f"Unsupported data type: {obj} ({type(obj).__name__})") - - -def is_primitive_type(val: Any) -> bool: - t = get_xml_type(val) - return t in {"str", "int", "float", "bool", "number", "null"} - - -def dict2xml_str( - attr_type: bool, - attr: Dict[str, Any], - item: Dict[str, Any], - item_func: Callable[[str], str], - cdata: bool, - item_name: str, - item_wrap: bool, - parentIsList: bool, -) -> str: - """ - dict2xml_str - """ - print( - "dict2xml_str", - type(attr_type), - type(attr), - type(item), - type(item_func), - type(cdata), - type(item_name), - type(item_wrap), - type(parentIsList), - ) - keys_str = ", ".join(str(key) for key in item) - LOG.info( - f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' item="{str(item)}"') - - if attr_type: - attr["type"] = get_xml_type(item) - attr = item.pop("@attrs", attr) # update attr with custom @attr if exists - rawitem = item["@val"] if "@val" in item else item - if is_primitive_type(rawitem): - subtree = rawitem - else: - # we can not use convert_dict, because rawitem could be non-dict - subtree = convert( - rawitem, ids, attr_type, item_func, cdata, item_wrap, item_name - ) - if item.get("@flat", False) or (parentIsList and not item_wrap): - return subtree - attrstring = make_attrstring(attr) - return f"<{item_name}{attrstring}>{subtree}" - - -def list2xml_str( - attr_type: bool, - attr: Dict[str, Any], - item: collections.abc.Iterable[Any], - item_func: Callable[[str], str], - cdata: bool, - item_name: str, - item_wrap: bool, -) -> str: - if isinstance(item, List) or isinstance(item, Set): - if attr_type: - attr["type"] = get_xml_type(item) - flat = False - if item_name.endswith("@flat"): - item_name = item_name[0:-5] - flat = True - subtree = convert_list( - items=item, - ids=ids, - parent=item_name, - attr_type=attr_type, - item_func=item_func, - cdata=cdata, - item_wrap=item_wrap, - ) - import pdb; pdb.set_trace() - if flat or (len(item) > 0 and is_primitive_type(item[0]) and not item_wrap): - return subtree - attrstring = make_attrstring(attr) - return f"<{item_name}{attrstring}>{subtree}" - - -def convert_dict( - obj: Dict[str, Any], - ids: List[str], - parent: str, - attr_type: bool, - item_func: Callable[[str], str], - cdata: bool, - item_wrap: bool, -) -> str: - """Converts a dict into an XML string.""" - keys_str = ", ".join(str(key) for key in obj) - LOG.info( - f'Inside convert_dict(): type(obj)="{type(obj).__name__}", keys="{keys_str}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') - - output: List[str] = [] - addline = output.append - - for key, val in obj.items(): - LOG.info( - f'Looping inside convert_dict(): key="{str(key)}", type(val)="{type(val).__name__}"' - ) - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' val="{str(val)}"') - - attr = {} if not ids else {"id": f"{get_unique_id(parent)}"} - - key, attr = make_valid_xml_name(key, attr) - - # since bool is also a subtype of number.Number and int, the check for bool - # never comes and hence we get wrong value for the xml type bool - # here, we just change order and check for bool first, because no other - # type other than bool can be true for bool check - if isinstance(val, bool): - addline(convert_bool(key, val, attr_type, attr, cdata)) - - elif isinstance(val, (numbers.Number, str)): - addline( - convert_kv( - key=key, val=val, attr_type=attr_type, attr=attr, cdata=cdata - ) - ) - - elif hasattr(val, "isoformat"): # datetime - addline( - convert_kv( - key=key, - val=val.isoformat(), - attr_type=attr_type, - attr=attr, - cdata=cdata, - ) - ) - - elif isinstance(val, dict): - addline( - dict2xml_str( - attr_type, attr, val, item_func, cdata, key, item_wrap, False - ) - ) - - elif isinstance(val, collections.abc.Iterable): - print("DEBUGNEW", type(item_func)) - addline( - list2xml_str( - attr_type=attr_type, - attr=attr, - item=val, - item_func=item_func, - cdata=cdata, - item_name=key, - item_wrap=item_wrap, - ) - ) - - elif not val: - addline(convert_none(key, val, attr_type, attr, cdata)) - - else: - raise TypeError(f"Unsupported data type: {val} ({type(val).__name__})") - - return "".join(output) - - -def convert_list( - items: collections.abc.Iterable[Any], - ids: List[str], - parent: str, - attr_type: bool, - item_func: Callable[[str], str], - cdata: bool, - item_wrap: bool, -) -> str: - """Converts a list into an XML string.""" - LOG.info(f'Inside convert_list(): type(items)="{type(items).__name__}"') - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' items="{str(items)}"') - - output: List[str] = [] - addline = output.append - - item_name = item_func(parent) - if item_name.endswith("@flat"): - item_name = item_name[:-5] - this_id = None - if ids: - this_id = get_unique_id(parent) - - for i, item in enumerate(items): - LOG.info( - f'Looping inside convert_list(): index="{str(i)}", type="{type(item).__name__}"' - ) - # avoid cpu consuming object serialization => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' item="{str(item)}"') - - attr = {} if not ids else {"id": f"{this_id}_{i + 1}"} - - print("DEBUG list_str", type(item_func)) - - if isinstance(item, bool): - addline(convert_bool(item_name, item, attr_type, attr, cdata)) - - elif isinstance(item, (numbers.Number, str)): - if item_wrap: - addline( - convert_kv( - key=item_name, - val=item, - attr_type=attr_type, - attr=attr, - cdata=cdata, - ) - ) - else: - addline( - convert_kv( - key=parent, - val=item, - attr_type=attr_type, - attr=attr, - cdata=cdata, - ) - ) - - elif hasattr(item, "isoformat"): # datetime - addline( - convert_kv( - key=item_name, - val=item.isoformat(), - attr_type=attr_type, - attr=attr, - cdata=cdata, - ) - ) - - elif isinstance(item, dict): - addline( - dict2xml_str( - attr_type, attr, item, item_func, cdata, item_name, item_wrap, True - ) - ) - - elif isinstance(item, collections.abc.Iterable): - print("DEBUGNEW", type(item_func)) - addline( - list2xml_str( - attr_type=attr_type, - attr=attr, - item=item, - item_func=item_func, - cdata=cdata, - item_name=item_name, - item_wrap=item_wrap, - ) - ) - - elif item is None: - addline(convert_none(item_name, None, attr_type, attr, cdata)) - - else: - raise TypeError(f"Unsupported data type: {item} ({type(item).__name__})") - return "".join(output) - - -def convert_kv( - key: str, - val: Union[str, numbers.Number], - attr_type: bool, - attr: Dict[str, Any] = {}, - cdata: bool = False, -) -> str: - """Converts a number or string into an XML element""" - LOG.info( - f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' - ) - print("convert_kv", type(val)) - key, attr = make_valid_xml_name(key, attr) - - if attr_type: - attr["type"] = get_xml_type(val) - attrstring = make_attrstring(attr) - return f"<{key}{attrstring}>{wrap_cdata(val) if cdata else escape_xml(val)}" - - -def convert_bool( - key: str, val: bool, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False -) -> str: - """Converts a boolean into an XML element""" - LOG.info( - f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' - ) - print(type(key), type(val), type(attr_type), type(attr), type(cdata)) - key, attr = make_valid_xml_name(key, attr) - - if attr_type: - attr["type"] = get_xml_type(val) - attrstring = make_attrstring(attr) - return f"<{key}{attrstring}>{str(val).lower()}" - - -def convert_none( - key: str, val: None, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False -) -> str: - """Converts a null value into an XML element""" - LOG.info(f'Inside convert_none(): key="{str(key)}"') - key, attr = make_valid_xml_name(key, attr) - - if attr_type: - attr["type"] = get_xml_type(val) - attrstring = make_attrstring(attr) - return f"<{key}{attrstring}>" - - -def dicttoxml( - obj: Dict[str, Any], - root: bool = True, - custom_root: str = "root", - ids: Optional[List[int]] = None, - attr_type: bool = True, - item_wrap: bool = True, - item_func: Callable[[str], str] = default_item_func, - cdata: bool = False, - xml_namespaces: dict[str, Any] = {}, -) -> bytes: - """Converts a python object into XML. - Arguments: - - root specifies whether the output is wrapped in an XML root element - Default is True - - custom_root allows you to specify a custom root element. - Default is 'root' - - ids specifies whether elements get unique ids. - Default is False - - attr_type specifies whether elements get a data type attribute. - Default is True - - item_func specifies what function should generate the element name for - items in a list. - Default is 'item' - - item_wrap specifies whether to nest items in array in - Default is True - - cdata specifies whether string values should be wrapped in CDATA sections. - Default is False - - xml_namespaces is a dictionary where key is xmlns prefix and value the urn, - e.g. { 'flex': 'http://www.w3.org/flex/flexBase', 'xsl': "http://www.w3.org/1999/XSL/Transform"} - results in - Default is {} - - Dictionaries-keys with special char '@' has special meaning: - @attrs: This allows custom xml attributes: {'@attr':{'a':'b'}, 'x':'y'} results in y - @flat: If a key ends with @flat (or dict contains key '@flat'), encapsulating node is omitted. Similar to item_wrap. - @val: @attrs requires complex dict type. If primitive type should be used, then @val is used as key. - To add custom xml-attributes on a list {'list': [4, 5, 6]}, you do this: - {'list': {'@attrs': {'a':'b','c':'d'}, '@val': [4, 5, 6]} - which results in 456 - """ - LOG.info( - f'Inside dicttoxml(): type(obj) is: "{type(obj).__name__}", type(ids") is : {type(ids).__name__}' - ) - # avoid cpu consuming object serialization (problem for large objects) => extra if - if LOG.getEffectiveLevel() <= logging.DEBUG: - LOG.debug(f' obj="{str(obj)}"') - - output = [] - namespacestr = "" - for prefix in xml_namespaces: - ns = xml_namespaces[prefix] - namespacestr += f' xmlns:{prefix}="{ns}"' - if root: - output.append('') - output_elem = convert( - obj, ids, attr_type, item_func, cdata, item_wrap, parent=custom_root - ) - output.append(f"<{custom_root}{namespacestr}>{output_elem}") - else: - output.append( - convert(obj, ids, attr_type, item_func, cdata, item_wrap, parent="") - ) - - return "".join(output).encode("utf-8") From 5df057b2837f50aa47bdee323659223724a3379d Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 00:45:40 +0530 Subject: [PATCH 5/9] cleanup imports --- json2xml/dicttoxml.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 9eb65cab..8ab27731 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -13,14 +13,12 @@ """ import collections +import datetime import logging import numbers -import types -from random import randint -from typing import Any, Dict, Union, Tuple, List, Optional, Set -from typing_extensions import reveal_type from collections.abc import Callable -import datetime +from random import randint +from typing import Any, Dict, List, Optional, Tuple, Union from defusedxml.minidom import parseString @@ -51,7 +49,16 @@ def get_unique_id(element: str) -> str: ELEMENT = Union[ - str, int, float, bool, numbers.Number, collections.abc.Sequence, datetime.datetime, datetime.date, None, Dict[str, Any] + str, + int, + float, + bool, + numbers.Number, + collections.abc.Sequence, + datetime.datetime, + datetime.date, + None, + Dict[str, Any], ] @@ -182,7 +189,9 @@ def convert( key=item_name, val=obj, attr_type=attr_type, attr={}, cdata=cdata ) - if hasattr(obj, "isoformat") and isinstance(obj, (datetime.datetime, datetime.date)): + if hasattr(obj, "isoformat") and isinstance( + obj, (datetime.datetime, datetime.date) + ): return convert_kv( key=item_name, val=obj.isoformat(), @@ -220,7 +229,7 @@ def dict2xml_str( parentIsList: bool, ) -> str: """ - dict2xml_str + parse dict2xml """ print( "dict2xml_str", @@ -514,7 +523,7 @@ def convert_bool( def convert_none( - key: str,attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False + key: str, attr_type: bool, attr: Dict[str, Any] = {}, cdata: bool = False ) -> str: """Converts a null value into an XML element""" # LOG.info(f'Inside convert_none(): key="{str(key)}" val={type(val)}') From 3efb33ae7f29777dbf9b46987822e246d4c5082e Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 00:59:59 +0530 Subject: [PATCH 6/9] feat: improve coverage --- tests/test_json2xml.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/test_json2xml.py b/tests/test_json2xml.py index b85f1d13..144ae4e8 100644 --- a/tests/test_json2xml.py +++ b/tests/test_json2xml.py @@ -12,7 +12,7 @@ import json from json2xml import json2xml -from json2xml.dicttoxml import dicttoxml +from json2xml import dicttoxml from json2xml.utils import InvalidDataError, readfromjson, readfromstring, readfromurl, \ JSONReadError, StringReadError, URLReadError @@ -157,17 +157,17 @@ def test_dicttoxml_bug(self): def test_dict2xml_no_root(self): payload = {'mock': 'payload'} - result = dicttoxml(payload, attr_type=False, root=False) + result = dicttoxml.dicttoxml(payload, attr_type=False, root=False) assert b'payload' == result def test_dict2xml_with_root(self): payload = {'mock': 'payload'} - result = dicttoxml(payload, attr_type=False) + result = dicttoxml.dicttoxml(payload, attr_type=False) assert b'payload' == result def test_dict2xml_with_custom_root(self): payload = {'mock': 'payload'} - result = dicttoxml(payload, attr_type=False, custom_root="element") + result = dicttoxml.dicttoxml(payload, attr_type=False, custom_root="element") assert b'payload' == result def test_bad_data(self): @@ -211,7 +211,7 @@ def test_read_boolean_data_from_json2(self): def test_dict2xml_with_namespaces(self): data = {'ns1:node1': 'data in namespace 1', 'ns2:node2': 'data in namespace 2'} namespaces = {'ns1': 'http://www.google.de/ns1', 'ns2': 'http://www.google.de/ns2'} - result = dicttoxml(data, attr_type=False, xml_namespaces=namespaces) + result = dicttoxml.dicttoxml(data, attr_type=False, xml_namespaces=namespaces) print(result) assert b'' \ b'' \ @@ -221,7 +221,7 @@ def test_dict2xml_with_namespaces(self): def test_dict2xml_with_flat(self): data = {'flat_list@flat': [1, 2, 3], 'non_flat_list': [4, 5, 6]} - result = dicttoxml(data, attr_type=False) + result = dicttoxml.dicttoxml(data, attr_type=False) print(result) assert b'' \ b'123' \ @@ -231,9 +231,13 @@ def test_dict2xml_with_flat(self): def test_dict2xml_with_val_and_custom_attr(self): # in order to use @attr in non-dict objects, we need to lift into a dict and combine with @val as key data = {'list1': [1, 2, 3], 'list2': {'@attrs': {'myattr1': 'myval1', 'myattr2': 'myval2'}, '@val': [4, 5, 6]}} - result = dicttoxml(data, attr_type=False) + result = dicttoxml.dicttoxml(data, attr_type=False) print(result) assert b'' \ b'123' \ b'456' \ b'' == result + + def test_make_id(self): + make_id_elem = dicttoxml.make_id("li") + assert 'li' in make_id_elem From 1f60c87769d490d114dc3ccd124944f5c84a8f42 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 01:09:46 +0530 Subject: [PATCH 7/9] feat: add two extra tests --- tests/test_json2xml.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_json2xml.py b/tests/test_json2xml.py index 144ae4e8..7ab03711 100644 --- a/tests/test_json2xml.py +++ b/tests/test_json2xml.py @@ -241,3 +241,16 @@ def test_dict2xml_with_val_and_custom_attr(self): def test_make_id(self): make_id_elem = dicttoxml.make_id("li") assert 'li' in make_id_elem + + def test_get_unique_id(self): + unique_id_elem_1 = dicttoxml.get_unique_id("li") + unique_id_elem_2 = dicttoxml.get_unique_id("li") + unique_id_elem_3 = dicttoxml.get_unique_id("li") + unique_id_elem_4 = dicttoxml.get_unique_id("li") + assert len(list(set({unique_id_elem_1, unique_id_elem_2, unique_id_elem_3, unique_id_elem_4}))) == 4 + + def test_get_xml_type(self): + assert dicttoxml.get_xml_type("abc") == "str" + assert dicttoxml.get_xml_type(1) == "int" + assert dicttoxml.get_xml_type(True) == "bool" + assert dicttoxml.get_xml_type({}) == "dict" From 5662c3966f5a423aa458776e2eac603fd087fe95 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 01:15:51 +0530 Subject: [PATCH 8/9] feat: remove unused export --- json2xml/dicttoxml.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 8ab27731..dd2da332 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -12,11 +12,10 @@ This module works with Python 3.7+ """ -import collections import datetime import logging import numbers -from collections.abc import Callable +from collections.abc import Callable, Sequence from random import randint from typing import Any, Dict, List, Optional, Tuple, Union @@ -54,7 +53,7 @@ def get_unique_id(element: str) -> str: float, bool, numbers.Number, - collections.abc.Sequence, + Sequence, datetime.datetime, datetime.date, None, @@ -78,7 +77,7 @@ def get_xml_type(val: ELEMENT) -> str: return "number" if isinstance(val, dict): return "dict" - if isinstance(val, collections.abc.Sequence): + if isinstance(val, Sequence): return "list" else: return "null" @@ -207,7 +206,7 @@ def convert( if isinstance(obj, dict): return convert_dict(obj, ids, parent, attr_type, item_func, cdata, item_wrap) - if isinstance(obj, collections.abc.Sequence): + if isinstance(obj, Sequence): return convert_list(obj, ids, parent, attr_type, item_func, cdata, item_wrap) raise TypeError(f"Unsupported data type: {obj} ({type(obj).__name__})") @@ -270,7 +269,7 @@ def dict2xml_str( def list2xml_str( attr_type: bool, attr: Dict[str, Any], - item: collections.abc.Sequence[Any], + item: Sequence[Any], item_func: Callable[[str], str], cdata: bool, item_name: str, @@ -361,7 +360,7 @@ def convert_dict( ) ) - elif isinstance(val, collections.abc.Sequence): + elif isinstance(val, Sequence): print("DEBUGNEW", type(item_func)) addline( list2xml_str( @@ -385,7 +384,7 @@ def convert_dict( def convert_list( - items: collections.abc.Sequence[Any], + items: Sequence[Any], ids: List[str], parent: str, attr_type: bool, @@ -464,7 +463,7 @@ def convert_list( ) ) - elif isinstance(item, collections.abc.Sequence): + elif isinstance(item, Sequence): print("DEBUGNEW", type(item_func)) addline( list2xml_str( From b195dad62fc55c23963bc0f6f62111b50c063f83 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Sun, 1 May 2022 01:17:45 +0530 Subject: [PATCH 9/9] chore: remove debug print statements --- json2xml/dicttoxml.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index dd2da332..896c1ef2 100755 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -39,10 +39,8 @@ def get_unique_id(element: str) -> str: while dup: if this_id not in ids: dup = False - print("get_unique_id", type(this_id)) ids.append(this_id) else: - print("get_unique_id", type(this_id)) this_id = make_id(element) return ids[-1] @@ -63,7 +61,6 @@ def get_unique_id(element: str) -> str: def get_xml_type(val: ELEMENT) -> str: """Returns the data type for the xml type attribute""" - print("get_xml_type", type(val)) if val is not None: if type(val).__name__ in ("str", "unicode"): return "str" @@ -199,7 +196,6 @@ def convert( cdata=cdata, ) - print("def convert attr_type", type(attr_type)) if obj is None: return convert_none(key=item_name, attr_type=attr_type, cdata=cdata) @@ -230,17 +226,6 @@ def dict2xml_str( """ parse dict2xml """ - print( - "dict2xml_str", - type(attr_type), - type(attr), - type(item), - type(item_func), - type(cdata), - type(item_name), - type(item_wrap), - type(parentIsList), - ) keys_str = ", ".join(str(key) for key in item) LOG.info( f'Inside dict_item2xml_str: type(obj)="{type(item).__name__}", keys="{keys_str}"' @@ -361,7 +346,6 @@ def convert_dict( ) elif isinstance(val, Sequence): - print("DEBUGNEW", type(item_func)) addline( list2xml_str( attr_type=attr_type, @@ -418,8 +402,6 @@ def convert_list( attr = {} if not ids else {"id": f"{this_id}_{i + 1}"} - print("DEBUG list_str", type(item_func)) - if isinstance(item, bool): addline(convert_bool(item_name, item, attr_type, attr, cdata)) @@ -464,7 +446,6 @@ def convert_list( ) elif isinstance(item, Sequence): - print("DEBUGNEW", type(item_func)) addline( list2xml_str( attr_type=attr_type, @@ -496,7 +477,6 @@ def convert_kv( LOG.info( f'Inside convert_kv(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) - print("convert_kv", type(val)) key, attr = make_valid_xml_name(key, attr) if attr_type: @@ -512,7 +492,6 @@ def convert_bool( LOG.info( f'Inside convert_bool(): key="{str(key)}", val="{str(val)}", type(val) is: "{type(val).__name__}"' ) - print(type(key), type(val), type(attr_type), type(attr), type(cdata)) key, attr = make_valid_xml_name(key, attr) if attr_type: