Source code for qal.common.xml_utils

"""
    Helper library for XML operations and their debugging. 
    
    .. note::
        Will probably be deprecated or at least rewritten when QAL switches completely to lxml.

    :copyright: Copyright 2010-2014 by Nicklas Boerjesson
    :license: BSD, see LICENSE for details. 
"""



from urllib.request import quote, unquote
from xml.dom.minidom import Text
from qal.sql.utils import check_for_param_content
from xml.dom.minidom import Document, parseString

[docs]def xml_set_cdata(_node, _value, _lowercase=False): """Helper to set character data in an XML tree""" if _value != None and _value != "": sec = Text() if _value is str: _value = quote(_value) if _lowercase: # Force lowercase. sec.data = _value.lower() else: sec.data = _value _node.appendChild(sec)
[docs]def xml_get_text(_node): """Helper function to get character data from an XML tree""" rc = list() for node in _node.childNodes: if node.nodeType == node.TEXT_NODE: rc.append(node.data) return unquote(''.join(rc))
[docs]def xml_get_boolean(_node): """Helper function to get a boolean value from XSD:booleans defaults""" value = xml_get_text(_node) if value.lower() == 'true': return True elif value.lower() == 'false': return False else: raise Exception('xml_get_boolean: "' + value + '" is not a boolean value')
[docs]def xml_get_numeric(_node, _type = ""): """Helper function to read a numeric value from the XML""" try: _value = xml_get_text(_node) if _type.lower() == "integer": number = int(_value) else: number = float(_value) except: if str(_value).lower() == 'null': return 'NULL' if not check_for_param_content(_value): raise Exception("xml_get_numeric: Invalid numeric format: " + _value) else: return _value return number
[docs]def xml_base_type_value(_node, _typename): """Using the base type name, get appropriate data""" if _typename in ["int", "float"]: return xml_get_numeric(_node) elif _typename in ["str"]: return xml_get_text(_node) elif _typename in ["bool"]: return xml_get_boolean(_node) else: raise Exception("xml_base_type_value: Invalid base type: " + _typename)
[docs]def xml_get_allowed_value(_node, _type): """Check if a value is allowed in a certain XML node""" value = xml_get_text(_node) if (value in _type[1] or value == ""): return value # Check for correct string format. elif value[0:8].lower() == "varchar(" and value[8:len(value) - 1].isnumeric() and value[len(value) - 1] == ")": return value else: raise Exception("xml_get_allowed_value: " + str(value) + " is not a valid value in a " + _type[0] + ". Node:" + str(_node))
[docs]def xml_find_non_text_child(_node): """Finds the first child that is not of the Text node type.""" nodelist = _node.childNodes for node in nodelist: if node.nodeType != node.TEXT_NODE: return node return None
[docs]def find_child_node(_node, _nodename): """Find all child nodes of an XML tree""" for node in _node.childNodes : # visit every node <bar /> if (node.nodeName.lower() == _nodename.lower()): return node return None
[docs]class XML_Translation(object): """ This is a base class for translations between XML structures and object structures. """ """Transcoding settings""" prefix_xml = 'xsi' prefix_schema = 'xsd' prefix_own = None namespace = None schema_uri = None encoding = 'utf-8' #Debugging debuglevel = 2 nestinglevel = 0 def __init__(self): ''' Constructor ''' pass """ Debugging """ def _print_nestinglevel(self, _value): """Prints the current nesting level. Not thread safe.""" self._debug_print(_value + ' level: ' + str(self.nestinglevel),4) def _get_up(self,_value): """Gets up one nesting level. Not thread safe.""" self.nestinglevel = self.nestinglevel - 1 self._print_nestinglevel("Leaving " + _value) def _go_down(self,_value): """Gets down one nesting level. Not thread safe.""" self.nestinglevel = self.nestinglevel + 1 self._print_nestinglevel("Entering " + _value) def _debug_print(self, _value, _debuglevel = 3): """Prints a debug message if the debugging level is sufficient.""" if self.debuglevel >= _debuglevel: print(_value) """Helper utilities""" def _add_own(self, _value): # Adds the set SQL-prefix. if self.prefix_own: return self.prefix_own + ':' + _value else: return _value def _strip_own(self, _value): ps_len = len(self.prefix_own + ':') if _value[0:ps_len] == self.prefix_own + ':': return _value[ps_len:len(_value)] else: return _value
[docs] def get_root_node(self, _nodename, _xml = "", _node = None ): if _node == None: self._debug_print(self.__class__.__name__ +".get_root_node : - XML being parsed:\n" + _xml) try: _doc = parseString(_xml) except Exception as e: raise Exception(self.__class__.__name__ +".get_root_node : Exception parsing SQL:\n" + str(e) + "\n XML: \n" + _xml) """ Find root node having _nodename.""" for _curr_node in _doc.childNodes: if _curr_node.nodeName == self._add_own(_nodename): return _curr_node if _curr_node.nodeName != self._add_own(_nodename): raise Exception(self.__class__.__name__ + '.get_root_node : "' + self._add_own(_nodename) + ' top node required.') else: if _node.nodeName != _nodename and _node.nodeName != self._add_own(_nodename): raise Exception(self.__class__.__name__ + '.get_root_node : "' + _nodename +'" top node required.') return _node
[docs]def xml_isnone(_node): if _node == None or _node.text == None: return None else: return _node.text