X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6f1a3f9c1a056e5ae063f020d38a5f40cc5e85ef..25b3661bd4cb44304418b93c0dee1d0dfb99765c:/wxPython/samples/ide/activegrid/util/xmlmarshaller.py diff --git a/wxPython/samples/ide/activegrid/util/xmlmarshaller.py b/wxPython/samples/ide/activegrid/util/xmlmarshaller.py index 1dcf3699e1..266418a1eb 100644 --- a/wxPython/samples/ide/activegrid/util/xmlmarshaller.py +++ b/wxPython/samples/ide/activegrid/util/xmlmarshaller.py @@ -12,20 +12,19 @@ import __builtin__ import sys from types import * +import logging import xml.sax import xml.sax.handler -from xml.sax import saxutils - -import objutils +import xml.sax.saxutils as saxutils +from activegrid.util.lang import * +import activegrid.util.aglogging as aglogging MODULE_PATH = "__main__" -### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed +## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed """ - -More documentation later, but here are some special Python attributes -that McLane recognizes: +Special attributes that we recognize: name: __xmlname__ type: string @@ -33,26 +32,26 @@ description: the name of the xml element for the marshalled object name: __xmlattributes__ type: tuple or list -description: the name(s) of the Python string attribute(s) to be +description: the name(s) of the Lang string attribute(s) to be marshalled as xml attributes instead of nested xml elements. currently -these can only be strings since there's not a way to get the type +these can only be strings since there"s not a way to get the type information back when unmarshalling. name: __xmlexclude__ type: tuple or list -description: the name(s) of the python attribute(s) to skip when +description: the name(s) of the lang attribute(s) to skip when marshalling. name: __xmlrename__ type: dict -description: describes an alternate Python <-> XML name mapping. +description: describes an alternate Lang <-> XML name mapping. Normally the name mapping is the identity function. __xmlrename__ -overrides that. The keys are the Python names, the values are their +overrides that. The keys are the Lang names, the values are their associated XML names. name: __xmlflattensequence__ type: dict, tuple, or list -description: the name(s) of the Python sequence attribute(s) whose +description: the name(s) of the Lang sequence attribute(s) whose items are to be marshalled as a series of xml elements (with an optional keyword argument that specifies the element name to use) as opposed to containing them in a separate sequence element, e.g.: @@ -60,12 +59,12 @@ opposed to containing them in a separate sequence element, e.g.: myseq = (1, 2) - 1 - 2 + 1 + 2 - -1 -2 + +1 +2 name: __xmlnamespaces__ type: dict @@ -82,10 +81,10 @@ should be used as the default namespace for the object. name: __xmlattrnamespaces__ type: dict -description: a dict assigning the Python object's attributes to the namespaces +description: a dict assigning the Lang object"s attributes to the namespaces defined in __xmlnamespaces__. Each item in the dict should consist of a prefix,attributeList combination where the key is the prefix and the value is -a list of the Python attribute names. e.g.: +a list of the Lang attribute names. e.g.: __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] } @@ -99,6 +98,14 @@ __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine """ +global xmlMarshallerLogger +xmlMarshallerLogger = logging.getLogger("activegrid.util.xmlmarshaller.marshal") +xmlMarshallerLogger.setLevel(aglogging.LEVEL_WARN) +# INFO : low-level info +# DEBUG : debugging info + +global knownGlobalTypes + ################################################################################ # # module exceptions @@ -119,7 +126,7 @@ class UnhandledTypeException(Error): return "%s is not supported for marshalling." % str(self.typename) class XMLAttributeIsNotStringType(Error): - """Exception raised when an object's attribute is specified to be + """Exception raised when an object"s attribute is specified to be marshalled as an XML attribute of the enclosing object instead of a nested element. """ @@ -128,27 +135,33 @@ class XMLAttributeIsNotStringType(Error): self.typename = typename def __str__(self): return """%s was set to be marshalled as an XML attribute - instead of a nested element, but the object's type is %s, not + instead of a nested element, but the object"s type is %s, not string.""" % (self.attrname, self.typename) +class MarshallerException(Exception): + pass + ################################################################################ # # constants and such # ################################################################################ -XMLNS = 'xmlns' -XMLNS_PREFIX = XMLNS + ':' +XMLNS = "xmlns" +XMLNS_PREFIX = XMLNS + ":" XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX) -BASETYPE_ELEMENT_NAME = 'item' +BASETYPE_ELEMENT_NAME = "item" +DICT_ITEM_NAME = "qqDictItem" +DICT_ITEM_KEY_NAME = "key" +DICT_ITEM_VALUE_NAME = "value" -# This list doesn't seem to be used. +# This list doesn"t seem to be used. # Internal documentation or useless? You make the call! -MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__', - '__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__', - '__xmldefaultnamespace__', '__xmlattrnamespaces__', - '__xmlattrgroups__') +##MEMBERS_TO_SKIP = ("__module__", "__doc__", "__xmlname__", "__xmlattributes__", +## "__xmlexclude__", "__xmlflattensequence__", "__xmlnamespaces__", +## "__xmldefaultnamespace__", "__xmlattrnamespaces__", +## "__xmlattrgroups__") ################################################################################ # @@ -156,71 +169,94 @@ MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__', # ################################################################################ +def setattrignorecase(object, name, value): + if (name not in object.__dict__): + namelow = name.lower() + for attr in object.__dict__: + if attr.lower() == namelow: + object.__dict__[attr] = value + return + object.__dict__[name] = value + +def getComplexType(obj): + if (hasattr(obj, "__xsdcomplextype__")): + return obj.__xsdcomplextype__ + return None + def _objectfactory(objname, objargs=None, xsname=None): - '''dynamically create an object based on the objname and return - it. look it up in the BASETYPE_ELEMENT_MAP first. - ''' - # split the objname into the typename and module path, - # importing the module if need be. + "dynamically create an object based on the objname and return it." + if not isinstance(objargs, list): objargs = [objargs] - - if (xsname): - try: - objname = knownGlobalTypes[xsname] - except KeyError: - pass - -## print "[objectfactory] creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname) - objtype = objname.split('.')[-1] - pathlist = objname.split('.') - modulename = '.'.join(pathlist[0:-1]) -## print "[objectfactory] object [%s] %s(%r)" % (objname, objtype, objargs) - if objname == 'bool': - return not objargs[0].lower() == 'false' - elif objname == 'str': # don't strip strings - blanks are significant !!! - if len(objargs) > 0: - return saxutils.unescape(objargs[0]).encode() - else: - return '' - elif objname == 'unicode': # don't strip strings - blanks are significant !!! +## print "[objectfactory] xsname [%s]; objname [%s]" % (xsname, objname) + + # (a) deal with tagName:knownTypes mappings + if (xsname != None): + objclass = knownGlobalTypes.get(xsname) + if (objclass != None): + if (objargs != None): + return objclass(*objargs) + else: + return objclass() + + # (b) next with intrinisic types + if objname == "str" or objname == "unicode": # don"t strip: blanks are significant if len(objargs) > 0: return saxutils.unescape(objargs[0]).encode() else: - return '' - elif objtype in ('float', 'int', 'str', 'long'): - objargs = [x.strip() for x in objargs] + return "" + elif objname == "bool": + return not objargs[0].lower() == "false" + elif objname in ("float", "int", "long"): +## objargs = [x.strip() for x in objargs] + return __builtin__.__dict__[objname](*objargs) + elif objname == "None": + return None + + # (c) objtype=path...module.class + # split the objname into the typename and module path, + # importing the module if need be. +## print "[objectfactory] creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname) + objtype = objname.split(".")[-1] + pathlist = objname.split(".") + modulename = ".".join(pathlist[0:-1]) +## print "[objectfactory] object [%s] %s(%r)" % (objname, objtype, objargs) try: - if __builtin__.__dict__.has_key(objname): - module = __builtin__ - else: - if modulename: - module = __import__(modulename) + if modulename: + module = __import__(modulename) for name in pathlist[1:-1]: module = module.__dict__[name] + elif __builtin__.__dict__.has_key(objname): + module = __builtin__ + else: + raise MarshallerException("Could not find class %s" % objname) if objargs: return module.__dict__[objtype](*objargs) else: - if objtype == 'None': - return None return module.__dict__[objtype]() except KeyError: - raise KeyError("Could not find class %s" % objname) + raise MarshallerException("Could not find class %s" % objname) class Element: + def __init__(self, name, attrs=None): self.name = name self.attrs = attrs - self.content = '' + self.content = "" self.children = [] + def getobjtype(self): - if self.attrs.has_key('objtype'): - return self.attrs.getValue('objtype') - else: - return 'str' - def toString(self): + objtype = self.attrs.get("objtype") + if (objtype == None): + if (len(self.children) > 0): + objtype = "dict" + else: + objtype = "str" + return objtype + + def __str__(self): print " name = ", self.name, "; attrs = ", self.attrs, "number of children = ", len(self.children) i = -1 for child in self.children: @@ -235,7 +271,7 @@ class XMLObjectFactory(xml.sax.ContentHandler): self.elementstack = [] xml.sax.handler.ContentHandler.__init__(self) - def toString(self): + def __str__(self): print "-----XMLObjectFactory Dump-------------------------------" if (self.rootelement == None): print "rootelement is None" @@ -246,14 +282,14 @@ class XMLObjectFactory(xml.sax.ContentHandler): for e in self.elementstack: i = i + 1 print "elementstack[", i, "]: " - e.toString() + str(e) print "-----end XMLObjectFactory--------------------------------" ## ContentHandler methods def startElement(self, name, attrs): ## print "startElement for name: ", name - if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd - name = name[name.index(':') + 1:] + if name.find(":") > -1: # Strip namespace prefixes for now until actually looking them up in xsd + name = name[name.find(":") + 1:] ## for attrname in attrs.getNames(): ## print "%s: %s" % (attrname, attrs.getValue(attrname)) element = Element(name, attrs.copy()) @@ -261,15 +297,15 @@ class XMLObjectFactory(xml.sax.ContentHandler): ## print self.elementstack def characters(self, content): -## print "got content: %s" % content - if content: +## print "got content: %s (%s)" % (content, type(content)) + if (content != None): self.elementstack[-1].content += content def endElement(self, name): -## print "[endElement] name of element we're at the end of: %s" % name +## print "[endElement] name of element we"re at the end of: %s" % name xsname = name - if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd - name = name[name.index(':') + 1:] + if name.find(":") > -1: # Strip namespace prefixes for now until actually looking them up in xsd + name = name[name.find(":") + 1:] oldChildren = self.elementstack[-1].children element = self.elementstack.pop() if ((len(self.elementstack) > 1) and (self.elementstack[-1].getobjtype() == "None")): @@ -283,29 +319,18 @@ class XMLObjectFactory(xml.sax.ContentHandler): ## print "[endElement] %s: skipping a (objtype==None) end tag" % name return constructorarglist = [] - if element.content: + if (len(element.content) > 0): strippedElementContent = element.content.strip() - if strippedElementContent: + if (len(strippedElementContent) > 0): constructorarglist.append(element.content) ## print "[endElement] calling objectfactory" obj = _objectfactory(objtype, constructorarglist, xsname) - complexType = None - if hasattr(obj, '__xsdcomplextype__'): - complexType = getattr(obj, '__xsdcomplextype__') - if (hasattr(obj, '__xmlname__') and getattr(obj, '__xmlname__') == "sequence"): -## print "[endElement] sequence found" -## self.toString() + complexType = getComplexType(obj) + if (obj != None): + if (hasattr(obj, "__xmlname__") and getattr(obj, "__xmlname__") == "sequence"): self.elementstack[-1].children = oldChildren -## self.toString() -## print "done moving sequence stuff; returning" return - if len(self.elementstack) > 0: -## print "[endElement] appending child with name: ", name, "; objtype: ", objtype - parentElement.children.append((name, obj)) -## print "parentElement now has ", len(parentElement.children), " children" - else: - self.rootelement = obj - if element.attrs and not isinstance(obj, list): + if (len(element.attrs) > 0) and not isinstance(obj, list): ## print "[endElement] %s: element has attrs and the obj is not a list" % name for attrname, attr in element.attrs.items(): if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX): @@ -313,77 +338,101 @@ class XMLObjectFactory(xml.sax.ContentHandler): ns = attrname[XMLNS_PREFIX_LENGTH:] else: ns = "" - if not hasattr(obj, '__xmlnamespaces__'): + if not hasattr(obj, "__xmlnamespaces__"): obj.__xmlnamespaces__ = {ns:attr} elif ns not in obj.__xmlnamespaces__: - if (hasattr(obj.__class__, '__xmlnamespaces__') - and obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__): - obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__) + if (hasattr(obj.__class__, "__xmlnamespaces__") + and (obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__)): + obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__) obj.__xmlnamespaces__[ns] = attr - elif not attrname == 'objtype': - if attrname.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd - attrname = attrname[attrname.index(':') + 1:] - if complexType: + elif not attrname == "objtype": + if attrname.find(":") > -1: # Strip namespace prefixes for now until actually looking them up in xsd + attrname = attrname[attrname.find(":") + 1:] + if (complexType != None): xsdElement = complexType.findElement(attrname) - if xsdElement: + if (xsdElement != None): type = xsdElement.type - if type: - type = xsdToPythonType(type) + if (type != None): + type = xsdToLangType(type) ### ToDO remove maxOccurs hack after bug 177 is fixed if attrname == "maxOccurs" and attr == "unbounded": attr = "-1" attr = _objectfactory(type, attr) - objutils.setattrignorecase(obj, _toAttrName(obj, attrname), attr) + try: + setattrignorecase(obj, _toAttrName(obj, attrname), attr) + except AttributeError: + raise MarshallerException("Error unmarshalling attribute \"%s\" of XML element \"%s\": object type not specified or known" % (attrname, name)) ## obj.__dict__[_toAttrName(obj, attrname)] = attr # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__ flattenDict = {} - if hasattr(obj, '__xmlflattensequence__'): + if hasattr(obj, "__xmlflattensequence__"): + flatten = obj.__xmlflattensequence__ ## print "[endElement] %s: obj has __xmlflattensequence__" % name - if (isinstance(obj.__xmlflattensequence__,dict)): -## print "[endElement] dict with obj.__xmlflattensequence__.items: ", obj.__xmlflattensequence__.items() - for sequencename, xmlnametuple in obj.__xmlflattensequence__.items(): - for xmlname in xmlnametuple: -## print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename) - flattenDict[xmlname] = sequencename - # handle __xmlflattensequence__ list/tuple (i.e. no element rename) - elif (isinstance(obj.__xmlflattensequence__,list) or isinstance(obj.__xmlflattensequence__,tuple)): - for sequencename in obj.__xmlflattensequence__: - flattenDict[sequencename] = sequencename + if (isinstance(flatten, dict)): +## print "[endElement] dict with flatten.items: ", flatten.items() + for sequencename, xmlnametuple in flatten.items(): + if (xmlnametuple == None): + flattenDict[sequencename] = sequencename + elif (not isinstance(xmlnametuple, (tuple, list))): + flattenDict[str(xmlnametuple)] = sequencename + else: + for xmlname in xmlnametuple: + ## print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename) + flattenDict[xmlname] = sequencename else: - raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple" + raise "Invalid type for __xmlflattensequence___ : it must be a dict" - # reattach an object's attributes to it + # reattach an object"s attributes to it for childname, child in element.children: ## print "[endElement] childname is: ", childname, "; child is: ", child - if flattenDict.has_key(childname): + if (childname in flattenDict): sequencename = _toAttrName(obj, flattenDict[childname]) ## print "[endElement] sequencename is: ", sequencename - try: + if (not hasattr(obj, sequencename)): ## print "[endElement] obj.__dict__ is: ", obj.__dict__ - sequencevalue = obj.__dict__[sequencename] - except (AttributeError, KeyError): - sequencevalue = None - if sequencevalue == None: - sequencevalue = [] - obj.__dict__[sequencename] = sequencevalue + obj.__dict__[sequencename] = [] + sequencevalue = getattr(obj, sequencename) + if (sequencevalue == None): + obj.__dict__[sequencename] = [] + sequencevalue = getattr(obj, sequencename) sequencevalue.append(child) - elif isinstance(obj, list): -## print "appended childname = ", childname + elif (objtype == "list"): obj.append(child) + elif isinstance(obj, dict): + if (childname == DICT_ITEM_NAME): + obj[child[DICT_ITEM_KEY_NAME]] = child[DICT_ITEM_VALUE_NAME] + else: + obj[childname] = child else: ## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child)) - objutils.setattrignorecase(obj, _toAttrName(obj, childname), child) - obj.__dict__[_toAttrName(obj, childname)] = child + try: + setattrignorecase(obj, _toAttrName(obj, childname), child) + except AttributeError: + raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname, name)) +## obj.__dict__[_toAttrName(obj, childname)] = child - if complexType: + if (complexType != None): for element in complexType.elements: if element.default: elementName = _toAttrName(obj, element.name) if ((elementName not in obj.__dict__) or (obj.__dict__[elementName] == None)): - pythonType = xsdToPythonType(element.type) - defaultValue = _objectfactory(pythonType, element.default) + langType = xsdToLangType(element.type) + defaultValue = _objectfactory(langType, element.default) obj.__dict__[elementName] = defaultValue + ifDefPy() + if (isinstance(obj, list)): + if ((element.attrs.has_key("mutable")) and (element.attrs.getValue("mutable") == "false")): + obj = tuple(obj) + endIfDef() + + if (len(self.elementstack) > 0): +## print "[endElement] appending child with name: ", name, "; objtype: ", objtype + parentElement.children.append((name, obj)) +## print "parentElement now has ", len(parentElement.children), " children" + else: + self.rootelement = obj + def getRootObject(self): return self.rootelement @@ -397,11 +446,11 @@ def _toAttrName(obj, name): ## name = "_%s%s" % (obj.__class__.__name__, name) return name -__typeMappingXsdToPython = { +__typeMappingXsdToLang = { "string": "str", "char": "str", "varchar": "str", - "date": "str", # ToDO Need to work out how to create python date types + "date": "str", # ToDO Need to work out how to create lang date types "boolean": "bool", "decimal": "float", # ToDO Does python have a better fixed point type? "int": "int", @@ -417,19 +466,19 @@ __typeMappingXsdToPython = { "double": "float", } -def xsdToPythonType(xsdType): - try: - return __typeMappingXsdToPython[xsdType] - except KeyError: +def xsdToLangType(xsdType): + langType = __typeMappingXsdToLang.get(xsdType) + if (langType == None): raise Exception("Unknown xsd type %s" % xsdType) + return langType -def _getXmlValue(pythonValue): - if (isinstance(pythonValue, bool)): - return str(pythonValue).lower() - elif (isinstance(pythonValue, unicode)): - return pythonValue.encode() +def _getXmlValue(langValue): + if (isinstance(langValue, bool)): + return str(langValue).lower() + elif (isinstance(langValue, unicode)): + return langValue.encode() else: - return str(pythonValue) + return str(langValue) def unmarshal(xmlstr, knownTypes=None): global knownGlobalTypes @@ -442,60 +491,61 @@ def unmarshal(xmlstr, knownTypes=None): return objectfactory.getRootObject() -def marshal(obj, elementName=None, prettyPrint=False, indent=0, knownTypes=None, withEncoding=True, encoding=None): - xmlstr = ''.join(_marshal(obj, elementName, prettyPrint=prettyPrint, indent=indent, knownTypes=knownTypes)) - if withEncoding: - if encoding is None: - return '\n%s' % (sys.getdefaultencoding(), xmlstr) - else: - return '\n%s' % (encoding, xmlstr.encode(encoding)) - else: +def marshal(obj, elementName=None, prettyPrint=False, indent=0, knownTypes=None, encoding=-1): + xmlstr = "".join(_marshal(obj, elementName, prettyPrint=prettyPrint, indent=indent, knownTypes=knownTypes)) + if (isinstance(encoding, basestring)): + return '\n%s' % (encoding, xmlstr.encode(encoding)) + elif (encoding == None): return xmlstr + else: + return '\n%s' % (sys.getdefaultencoding(), xmlstr) -def _marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0, knownTypes=None): +def _marshal(obj, elementName=None, nameSpacePrefix="", nameSpaces=None, prettyPrint=False, indent=0, knownTypes=None): + xmlMarshallerLogger.debug("--> _marshal: elementName=%s, type=%s, obj=%s" % (elementName, type(obj), str(obj))) + xmlString = None if prettyPrint or indent: - prefix = ' '*indent - newline = '\n' + prefix = " "*indent + newline = "\n" increment = 4 else: - prefix = '' - newline = '' + prefix = "" + newline = "" increment = 0 - ## Determine the XML element name. If it isn't specified in the - ## parameter list, look for it in the __xmlname__ Python + ## Determine the XML element name. If it isn"t specified in the + ## parameter list, look for it in the __xmlname__ Lang ## attribute, else use the default generic BASETYPE_ELEMENT_NAME. if not nameSpaces: nameSpaces = {} # Need to do this since if the {} is a default parameter it gets shared by all calls into the function - nameSpaceAttrs = '' + nameSpaceAttrs = "" if knownTypes == None: knownTypes = {} - if hasattr(obj, '__xmlnamespaces__'): - for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items(): - if nameSpaceUrl in nameSpaces: + if hasattr(obj, "__xmlnamespaces__"): + for nameSpaceKey, nameSpaceUrl in getattr(obj, "__xmlnamespaces__").items(): + if nameSpaceUrl in asDict(nameSpaces): nameSpaceKey = nameSpaces[nameSpaceUrl] else: -## # TODO: Wait to do this until there is shared state for use when going through the object graph -## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's +## # TODO: Wait to do this until there is shared for use when going through the object graph +## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL"s ## i = 1 ## while nameSpaceKey in nameSpaces.values(): ## nameSpaceKey = origNameSpaceKey + str(i) ## i += 1 nameSpaces[nameSpaceUrl] = nameSpaceKey - if nameSpaceKey == '': + if nameSpaceKey == "": nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl) else: nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl) nameSpaceAttrs = nameSpaceAttrs.rstrip() - if hasattr(obj, '__xmldefaultnamespace__'): - nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':' + if hasattr(obj, "__xmldefaultnamespace__"): + nameSpacePrefix = getattr(obj, "__xmldefaultnamespace__") + ":" if not elementName: - if hasattr(obj, '__xmlname__'): + if hasattr(obj, "__xmlname__"): elementName = nameSpacePrefix + obj.__xmlname__ else: elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME else: elementName = nameSpacePrefix + elementName - if hasattr(obj, '__xmlsequencer__'): + if hasattr(obj, "__xmlsequencer__"): elementAdd = obj.__xmlsequencer__ else: elementAdd = None @@ -504,42 +554,41 @@ def _marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyP members_to_skip = [] ## Add more members_to_skip based on ones the user has selected ## via the __xmlexclude__ attribute. - if hasattr(obj, '__xmlexclude__'): + if hasattr(obj, "__xmlexclude__"): ## print "marshal: found __xmlexclude__" - members_to_skip += list(obj.__xmlexclude__) + members_to_skip.extend(obj.__xmlexclude__) # Marshal the attributes that are selected to be XML attributes. - objattrs = '' - className = obj.__class__.__name__ + objattrs = "" + className = ag_className(obj) classNamePrefix = "_" + className - if hasattr(obj, '__xmlattributes__'): + if hasattr(obj, "__xmlattributes__"): ## print "marshal: found __xmlattributes__" xmlattributes = obj.__xmlattributes__ - members_to_skip += xmlattributes + members_to_skip.extend(xmlattributes) for attr in xmlattributes: internalAttrName = attr + ifDefPy() if (attr.startswith("__") and not attr.endswith("__")): internalAttrName = classNamePrefix + attr + endIfDef() # Fail silently if a python attribute is specified to be # an XML attribute but is missing. ## print "marshal: processing attribute ", internalAttrName - try: - value = obj.__dict__[internalAttrName] - except KeyError: - continue -## # But, check and see if it is a property first: -## if (objutils.hasPropertyValue(obj, attr)): -## value = getattr(obj, attr) -## else: -## continue + attrs = obj.__dict__ + value = attrs.get(internalAttrName) xsdElement = None - if hasattr(obj, '__xsdcomplextype__'): + complexType = getComplexType(obj) + if (complexType != None): ## print "marshal: found __xsdcomplextype__" - complexType = getattr(obj, '__xsdcomplextype__') xsdElement = complexType.findElement(attr) - if xsdElement: + if (xsdElement != None): default = xsdElement.default - if default == value or default == _getXmlValue(value): - continue + if (default != None): + if ((default == value) or (default == _getXmlValue(value))): + continue + else: + if (value == None): + continue elif value == None: continue @@ -553,184 +602,205 @@ def _marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyP else: value = "false" - attrNameSpacePrefix = '' - if hasattr(obj, '__xmlattrnamespaces__'): + attrNameSpacePrefix = "" + if hasattr(obj, "__xmlattrnamespaces__"): ## print "marshal: found __xmlattrnamespaces__" - for nameSpaceKey, nameSpaceAttributes in getattr(obj, '__xmlattrnamespaces__').items(): - if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as it's element + for nameSpaceKey, nameSpaceAttributes in getattr(obj, "__xmlattrnamespaces__").iteritems(): + if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as its element continue if attr in nameSpaceAttributes: - attrNameSpacePrefix = nameSpaceKey + ':' + attrNameSpacePrefix = nameSpaceKey + ":" break -## if attr.startswith('_'): +## if attr.startswith("_"): ## attr = attr[1:] - if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__): + if (hasattr(obj, "__xmlrename__") and attr in asDict(obj.__xmlrename__)): ## print "marshal: found __xmlrename__ (and its attribute)" attr = obj.__xmlrename__[attr] - objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value) + objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, str(value)) ## print "marshal: new objattrs is: ", objattrs - if isinstance(obj, NoneType): - return '' + if (obj == None): + xmlString = [""] elif isinstance(obj, bool): - return ['%s<%s objtype="bool">%s%s' % (prefix, elementName, obj, elementName, newline)] + xmlString = ['%s<%s objtype="bool">%s%s' % (prefix, elementName, obj, elementName, newline)] elif isinstance(obj, int): - return ['''%s<%s objtype="int">%s%s''' % (prefix, elementName, str(obj), elementName, newline)] + xmlString = ['%s<%s objtype="int">%s%s' % (prefix, elementName, str(obj), elementName, newline)] elif isinstance(obj, long): - return ['%s<%s objtype="long">%s%s' % (prefix, elementName, str(obj), elementName, newline)] + xmlString = ['%s<%s objtype="long">%s%s' % (prefix, elementName, str(obj), elementName, newline)] elif isinstance(obj, float): - return ['%s<%s objtype="float">%s%s' % (prefix, elementName, str(obj), elementName, newline)] + xmlString = ['%s<%s objtype="float">%s%s' % (prefix, elementName, str(obj), elementName, newline)] elif isinstance(obj, unicode): # have to check before basestring - unicode is instance of base string - return ['''%s<%s>%s%s''' % (prefix, elementName, saxutils.escape(obj.encode()), elementName, newline)] + xmlString = ['%s<%s>%s%s' % (prefix, elementName, saxutils.escape(obj.encode()), elementName, newline)] elif isinstance(obj, basestring): - return ['''%s<%s>%s%s''' % (prefix, elementName, saxutils.escape(obj), elementName, newline)] + xmlString = ['%s<%s>%s%s' % (prefix, elementName, saxutils.escape(obj), elementName, newline)] elif isinstance(obj, list): if len(obj) < 1: - return '' - xmlString = ['%s<%s objtype="list">%s' % (prefix, elementName, newline)] - for item in obj: - xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) - xmlString.append('%s%s' % (prefix, elementName, newline)) - return xmlString + xmlString = "" + else: + xmlString = ['%s<%s objtype="list">%s' % (prefix, elementName, newline)] + for item in obj: + xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) + xmlString.append("%s%s" % (prefix, elementName, newline)) elif isinstance(obj, tuple): if len(obj) < 1: - return '' - xmlString = ['%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)] - for item in obj: - xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) - xmlString.append('%s%s' % (prefix, elementName, newline)) - return xmlString + xmlString = "" + else: + xmlString = ['%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)] + for item in obj: + xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) + xmlString.append("%s%s" % (prefix, elementName, newline)) elif isinstance(obj, dict): xmlString = ['%s<%s objtype="dict">%s' % (prefix, elementName, newline)] - subprefix = prefix + ' '*increment + subprefix = prefix + " "*increment subindent = indent + 2*increment for key, val in obj.iteritems(): - xmlString.append("%s%s" % (subprefix, newline)) - xmlString.extend(_marshal(key, indent=subindent, knownTypes=knownTypes)) - xmlString.append("%s%s%s%s" % (subprefix, newline, subprefix, newline)) - xmlString.extend(_marshal(val, nameSpaces=nameSpaces, indent=subindent, knownTypes=knownTypes)) - xmlString.append("%s%s" % (subprefix, newline)) - xmlString.append('%s%s' % (prefix, elementName, newline)) - return xmlString +## if (isinstance(key, basestring) and key is legal identifier): +## xmlString.extend(_marshal(val, elementName=key, nameSpaces=nameSpaces, indent=subindent, knownTypes=knownTypes)) +## else: + xmlString.append("%s<%s>%s" % (subprefix, DICT_ITEM_NAME, newline)) + xmlString.extend(_marshal(key, elementName=DICT_ITEM_KEY_NAME, indent=subindent, knownTypes=knownTypes)) + xmlString.extend(_marshal(val, elementName=DICT_ITEM_VALUE_NAME, nameSpaces=nameSpaces, indent=subindent, knownTypes=knownTypes)) + xmlString.append("%s%s" % (subprefix, DICT_ITEM_NAME, newline)) + xmlString.append("%s%s" % (prefix, elementName, newline)) else: - moduleName = obj.__class__.__module__ - if (moduleName == "activegrid.model.schema"): - xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)] + # Only add the objtype if the element tag is unknown to us. + objname = knownTypes.get(elementName) + if (objname != None): + xmlString = ["%s<%s%s%s" % (prefix, elementName, nameSpaceAttrs, objattrs)] else: - # Only add the objtype if the element tag is unknown to us. - try: - objname = knownTypes[elementName] - xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)] - except KeyError: - xmlString = ['%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, moduleName, className)] -## print "UnknownTypeException: Unknown type (%s.%s) passed to marshaller" % (moduleName, className) - # get the member, value pairs for the object, filtering out the types we don't support + xmlString = ['%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, obj.__class__.__module__, className)] + # get the member, value pairs for the object, filtering out the types we don"t support if (elementAdd != None): - prefix += increment*' ' + prefix += increment*" " indent += increment xmlMemberString = [] - if hasattr(obj, '__xmlbody__'): + if hasattr(obj, "__xmlbody__"): xmlbody = getattr(obj, obj.__xmlbody__) if xmlbody != None: xmlMemberString.append(xmlbody) else: - entryList = obj.__dict__.items() -## # Add in properties -## for key in obj.__class__.__dict__.iterkeys(): -## if (key not in members_to_skip and key not in obj.__dict__ -## and objutils.hasPropertyValue(obj, key)): -## value = getattr(obj, key) -## entryList.append((key, value)) - entryList.sort() - if hasattr(obj, '__xmlattrgroups__'): - attrGroups = obj.__xmlattrgroups__ - if (not isinstance(attrGroups,dict)): + if hasattr(obj, "__xmlattrgroups__"): + attrGroups = obj.__xmlattrgroups__.copy() + if (not isinstance(attrGroups, dict)): raise "__xmlattrgroups__ is not a dict, but must be" - for n in attrGroups: - v = attrGroups[n] - members_to_skip += v + for n in attrGroups.iterkeys(): + members_to_skip.extend(attrGroups[n]) else: attrGroups = {} # add the list of all attributes to attrGroups - eList = [] - for x, z in entryList: - eList.append(x) - attrGroups['__nogroup__'] = eList + eList = obj.__dict__.keys() + eList.sort() + attrGroups["__nogroup__"] = eList - for eName in attrGroups: - eList = attrGroups[eName] - if (eName != '__nogroup__'): - prefix += increment*' ' + for eName, eList in attrGroups.iteritems(): + if (eName != "__nogroup__"): + prefix += increment*" " indent += increment xmlMemberString.append('%s<%s objtype="None">%s' % (prefix, eName, newline)) for name in eList: value = obj.__dict__[name] -## print " ", name, " = ", value -## # special name handling for private "__*" attributes: -## # remove the _ added by Python -## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):] - if eName == '__nogroup__' and name in members_to_skip: continue - if name.startswith('__') and name.endswith('__'): continue -## idx = name.find('__') -## if idx > 0: -## newName = name[idx+2:] -## if newName: -## name = newName -## print "marshal: processing subElement ", name + if eName == "__nogroup__" and name in members_to_skip: continue + if name.startswith("__") and name.endswith("__"): continue subElementNameSpacePrefix = nameSpacePrefix - if hasattr(obj, '__xmlattrnamespaces__'): - for nameSpaceKey, nameSpaceValues in getattr(obj, '__xmlattrnamespaces__').items(): + if hasattr(obj, "__xmlattrnamespaces__"): + for nameSpaceKey, nameSpaceValues in getattr(obj, "__xmlattrnamespaces__").iteritems(): if name in nameSpaceValues: - subElementNameSpacePrefix = nameSpaceKey + ':' + subElementNameSpacePrefix = nameSpaceKey + ":" break # handle sequences listed in __xmlflattensequence__ # specially: instead of listing the contained items inside - # of a separate list, as god intended, list them inside + # of a separate list, as God intended, list them inside # the object containing the sequence. - if hasattr(obj, '__xmlflattensequence__') and name in obj.__xmlflattensequence__ and value: - try: - xmlnametuple = obj.__xmlflattensequence__[name] - xmlname = None - if len(xmlnametuple) == 1: - xmlname = xmlnametuple[0] - except: - xmlname = name -## xmlname = name.lower() + if (hasattr(obj, "__xmlflattensequence__") and (value != None) and (name in asDict(obj.__xmlflattensequence__))): + xmlnametuple = obj.__xmlflattensequence__[name] + if (xmlnametuple == None): + xmlnametuple = [name] + elif (not isinstance(xmlnametuple, (tuple,list))): + xmlnametuple = [str(xmlnametuple)] + xmlname = None + if (len(xmlnametuple) == 1): + xmlname = xmlnametuple[0] +## ix = 0 for seqitem in value: +## xmlname = xmlnametuple[ix] +## ix += 1 +## if (ix >= len(xmlnametuple)): +## ix = 0 xmlMemberString.extend(_marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) else: - if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__): + if (hasattr(obj, "__xmlrename__") and name in asDict(obj.__xmlrename__)): xmlname = obj.__xmlrename__[name] else: xmlname = name -## if (indent > 30): -## print "getting pretty deep, xmlname = ", xmlname xmlMemberString.extend(_marshal(value, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)) - if (eName != '__nogroup__'): -## print "marshal: Completing attrGroup ", eName - xmlMemberString.append('%s%s' % (prefix, eName, newline)) + if (eName != "__nogroup__"): + xmlMemberString.append("%s%s" % (prefix, eName, newline)) prefix = prefix[:-increment] indent -= increment # if we have nested elements, add them here, otherwise close the element tag immediately. - xmlMemberString = filter(lambda x: len(x)>0, xmlMemberString) + newList = [] + for s in xmlMemberString: + if (len(s) > 0): newList.append(s) + xmlMemberString = newList if len(xmlMemberString) > 0: - xmlString.append('>') - if hasattr(obj, '__xmlbody__'): + xmlString.append(">") + if hasattr(obj, "__xmlbody__"): xmlString.extend(xmlMemberString) - xmlString.append('%s' % (elementName, newline)) + xmlString.append("%s" % (elementName, newline)) else: xmlString.append(newline) if (elementAdd != None): - xmlString.append('%s<%s>%s' % (prefix, elementAdd, newline)) + xmlString.append("%s<%s>%s" % (prefix, elementAdd, newline)) xmlString.extend(xmlMemberString) if (elementAdd != None): - xmlString.append('%s%s' % (prefix, elementAdd, newline)) + xmlString.append("%s%s" % (prefix, elementAdd, newline)) prefix = prefix[:-increment] indent -= increment - xmlString.append('%s%s' % (prefix, elementName, newline)) + xmlString.append("%s%s" % (prefix, elementName, newline)) else: - xmlString.append('/>%s' % newline) - return xmlString + xmlString.append("/>%s" % newline) +## return xmlString + xmlMarshallerLogger.debug("<-- _marshal: %s" % str(xmlString)) + return xmlString + +# A simple test, to be executed when the xmlmarshaller is run standalone +class MarshallerPerson: + __xmlname__ = "person" + __xmlexclude__ = ["fabulousness",] + __xmlattributes__ = ("nonSmoker",) + __xmlrename__ = {"_phoneNumber": "telephone"} + __xmlflattensequence__ = {"favoriteWords": ("vocabulary",)} + __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]} + + def setPerson(self): + self.firstName = "Albert" + self.lastName = "Camus" + self.addressLine1 = "23 Absurd St." + self.city = "Ennui" + self.state = "MO" + self.zip = "54321" + self._phoneNumber = "808-303-2323" + self.favoriteWords = ["angst", "ennui", "existence"] + self.phobias = ["war", "tuberculosis", "cars"] + self.weight = 150 + self.fabulousness = "tres tres" + self.nonSmoker = False + +if isMain(__name__): + p1 = MarshallerPerson() + p1.setPerson() + xmlP1 = marshal(p1, prettyPrint=True, encoding="utf-8") + print "\n########################" + print "# testPerson test case #" + print "########################" + print xmlP1 + p2 = unmarshal(xmlP1) + xmlP2 = marshal(p2, prettyPrint=True, encoding="utf-8") + if xmlP1 == xmlP2: + print "Success: repeated marshalling yields identical results" + else: + print "Failure: repeated marshalling yields different results" + print xmlP2