1 #----------------------------------------------------------------------------
2 # Name: xmlmarshaller.py
5 # Author: John Spurling
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
16 import xml
.sax
.handler
17 from xml
.sax
import saxutils
21 MODULE_PATH
= "__main__"
23 ### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
27 More documentation later, but here are some special Python attributes
28 that McLane recognizes:
32 description: the name of the xml element for the marshalled object
34 name: __xmlattributes__
36 description: the name(s) of the Python string attribute(s) to be
37 marshalled as xml attributes instead of nested xml elements. currently
38 these can only be strings since there's not a way to get the type
39 information back when unmarshalling.
43 description: the name(s) of the python attribute(s) to skip when
48 description: describes an alternate Python <-> XML name mapping.
49 Normally the name mapping is the identity function. __xmlrename__
50 overrides that. The keys are the Python names, the values are their
53 name: __xmlflattensequence__
54 type: dict, tuple, or list
55 description: the name(s) of the Python sequence attribute(s) whose
56 items are to be marshalled as a series of xml elements (with an
57 optional keyword argument that specifies the element name to use) as
58 opposed to containing them in a separate sequence element, e.g.:
61 <!-- normal way of marshalling -->
63 <item objtype='int'>1</item>
64 <item objtype='int'>2</item>
66 <!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
67 <squish objtype='int'>1</squish>
68 <squish objtype='int'>2</squish>
70 name: __xmlnamespaces__
72 description: a dict of the namespaces that the object uses. Each item
73 in the dict should consist of a prefix,url combination where the key is
74 the prefix and url is the value, e.g.:
76 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
78 name: __xmldefaultnamespace__
80 description: the prefix of a namespace defined in __xmlnamespaces__ that
81 should be used as the default namespace for the object.
83 name: __xmlattrnamespaces__
85 description: a dict assigning the Python object's attributes to the namespaces
86 defined in __xmlnamespaces__. Each item in the dict should consist of a
87 prefix,attributeList combination where the key is the prefix and the value is
88 a list of the Python attribute names. e.g.:
90 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
92 name: __xmlattrgroups__
94 description: a dict specifying groups of attributes to be wrapped in an enclosing tag.
95 The key is the name of the enclosing tag; the value is a list of attributes to include
98 __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
102 ################################################################################
106 ################################################################################
108 class Error(Exception):
109 """Base class for errors in this module."""
112 class UnhandledTypeException(Error
):
113 """Exception raised when attempting to marshal an unsupported
116 def __init__(self
, typename
):
117 self
.typename
= typename
119 return "%s is not supported for marshalling." % str(self
.typename
)
121 class XMLAttributeIsNotStringType(Error
):
122 """Exception raised when an object's attribute is specified to be
123 marshalled as an XML attribute of the enclosing object instead of
126 def __init__(self
, attrname
, typename
):
127 self
.attrname
= attrname
128 self
.typename
= typename
130 return """%s was set to be marshalled as an XML attribute
131 instead of a nested element, but the object's type is %s, not
132 string.""" % (self
.attrname
, self
.typename
)
134 ################################################################################
138 ################################################################################
141 XMLNS_PREFIX
= XMLNS
+ ':'
142 XMLNS_PREFIX_LENGTH
= len(XMLNS_PREFIX
)
144 BASETYPE_ELEMENT_NAME
= 'item'
146 # This list doesn't seem to be used.
147 # Internal documentation or useless? You make the call!
148 MEMBERS_TO_SKIP
= ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
149 '__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
150 '__xmldefaultnamespace__', '__xmlattrnamespaces__',
153 ################################################################################
155 # classes and functions
157 ################################################################################
159 def _objectfactory(objname
, objargs
=None, xsname
=None):
160 '''dynamically create an object based on the objname and return
161 it. look it up in the BASETYPE_ELEMENT_MAP first.
163 # split the objname into the typename and module path,
164 # importing the module if need be.
165 if not isinstance(objargs
, list):
170 objname
= knownGlobalTypes
[xsname
]
174 ## print "[objectfactory] creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
175 objtype
= objname
.split('.')[-1]
176 pathlist
= objname
.split('.')
177 modulename
= '.'.join(pathlist
[0:-1])
179 ## print "[objectfactory] object [%s] %s(%r)" % (objname, objtype, objargs)
180 if objname
== 'bool':
181 return not objargs
[0].lower() == 'false'
182 elif objname
== 'str': # don't strip strings - blanks are significant !!!
184 return saxutils
.unescape(objargs
[0]).encode()
187 elif objname
== 'unicode': # don't strip strings - blanks are significant !!!
189 return saxutils
.unescape(objargs
[0]).encode()
192 elif objtype
in ('float', 'int', 'str', 'long'):
193 objargs
= [x
.strip() for x
in objargs
]
196 if __builtin__
.__dict
__.has_key(objname
):
198 elif knownGlobalModule
:
199 module
= knownGlobalModule
202 module
= __import__(modulename
)
203 for name
in pathlist
[1:-1]:
204 module
= module
.__dict
__[name
]
206 return module
.__dict
__[objtype
](*objargs
)
208 if objtype
== 'None':
210 return module
.__dict
__[objtype
]()
212 raise KeyError("Could not find class %s" % objname
)
215 def __init__(self
, name
, attrs
=None):
220 def getobjtype(self
):
221 if self
.attrs
.has_key('objtype'):
222 return self
.attrs
.getValue('objtype')
226 print " name = ", self
.name
, "; attrs = ", self
.attrs
, "number of children = ", len(self
.children
)
228 for child
in self
.children
:
230 childClass
= child
.__class
__.__name
__
231 print " Child ", i
, " class: ",childClass
234 class XMLObjectFactory(xml
.sax
.ContentHandler
):
236 self
.rootelement
= None
237 self
.elementstack
= []
238 xml
.sax
.handler
.ContentHandler
.__init
__(self
)
241 print "-----XMLObjectFactory Dump-------------------------------"
242 if (self
.rootelement
== None):
243 print "rootelement is None"
245 print "rootelement is an object"
247 print "length of elementstack is: ", len(self
.elementstack
)
248 for e
in self
.elementstack
:
250 print "elementstack[", i
, "]: "
252 print "-----end XMLObjectFactory--------------------------------"
254 ## ContentHandler methods
255 def startElement(self
, name
, attrs
):
256 ## print "startElement for name: ", name
257 if name
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
258 name
= name
[name
.index(':') + 1:]
259 ## for attrname in attrs.getNames():
260 ## print "%s: %s" % (attrname, attrs.getValue(attrname))
261 element
= Element(name
, attrs
.copy())
262 self
.elementstack
.append(element
)
263 ## print self.elementstack
265 def characters(self
, content
):
266 ## print "got content: %s" % content
268 self
.elementstack
[-1].content
+= content
270 def endElement(self
, name
):
271 ## print "[endElement] name of element we're at the end of: %s" % name
273 if name
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
274 name
= name
[name
.index(':') + 1:]
275 oldChildren
= self
.elementstack
[-1].children
276 element
= self
.elementstack
.pop()
277 if ((len(self
.elementstack
) > 1) and (self
.elementstack
[-1].getobjtype() == "None")):
278 parentElement
= self
.elementstack
[-2]
279 ## print "[endElement] %s: found parent with objtype==None: using its grandparent" % name
280 elif (len(self
.elementstack
) > 0):
281 parentElement
= self
.elementstack
[-1]
282 objtype
= element
.getobjtype()
283 ## print "element objtype is: ", objtype
284 if (objtype
== "None"):
285 ## print "[endElement] %s: skipping a (objtype==None) end tag" % name
287 constructorarglist
= []
289 strippedElementContent
= element
.content
.strip()
290 if strippedElementContent
:
291 constructorarglist
.append(element
.content
)
292 ## print "[endElement] calling objectfactory"
293 obj
= _objectfactory(objtype
, constructorarglist
, xsname
)
295 if hasattr(obj
, '__xsdcomplextype__'):
296 complexType
= getattr(obj
, '__xsdcomplextype__')
297 if (hasattr(obj
, '__xmlname__') and getattr(obj
, '__xmlname__') == "sequence"):
298 ## print "[endElement] sequence found"
300 self
.elementstack
[-1].children
= oldChildren
302 ## print "done moving sequence stuff; returning"
304 if len(self
.elementstack
) > 0:
305 ## print "[endElement] appending child with name: ", name, "; objtype: ", objtype
306 parentElement
.children
.append((name
, obj
))
307 ## print "parentElement now has ", len(parentElement.children), " children"
309 self
.rootelement
= obj
310 if element
.attrs
and not isinstance(obj
, list):
311 ## print "[endElement] %s: element has attrs and the obj is not a list" % name
312 for attrname
, attr
in element
.attrs
.items():
313 if attrname
== XMLNS
or attrname
.startswith(XMLNS_PREFIX
):
314 if attrname
.startswith(XMLNS_PREFIX
):
315 ns
= attrname
[XMLNS_PREFIX_LENGTH
:]
318 if not hasattr(obj
, '__xmlnamespaces__'):
319 obj
.__xmlnamespaces
__ = {ns:attr}
320 elif ns
not in obj
.__xmlnamespaces
__:
321 if (hasattr(obj
.__class
__, '__xmlnamespaces__')
322 and obj
.__xmlnamespaces
__ is obj
.__class
__.__xmlnamespaces
__):
323 obj
.__xmlnamespaces
__ = dict(obj
.__xmlnamespaces
__)
324 obj
.__xmlnamespaces
__[ns
] = attr
325 elif not attrname
== 'objtype':
326 if attrname
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
327 attrname
= attrname
[attrname
.index(':') + 1:]
329 xsdElement
= complexType
.findElement(attrname
)
331 type = xsdElement
.type
333 type = xsdToPythonType(type)
334 ### ToDO remove maxOccurs hack after bug 177 is fixed
335 if attrname
== "maxOccurs" and attr
== "unbounded":
337 attr
= _objectfactory(type, attr
)
338 objutils
.setattrignorecase(obj
, _toAttrName(obj
, attrname
), attr
)
339 ## obj.__dict__[_toAttrName(obj, attrname)] = attr
340 # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
342 if hasattr(obj
, '__xmlflattensequence__'):
343 ## print "[endElement] %s: obj has __xmlflattensequence__" % name
344 if (isinstance(obj
.__xmlflattensequence
__,dict)):
345 ## print "[endElement] dict with obj.__xmlflattensequence__.items: ", obj.__xmlflattensequence__.items()
346 for sequencename
, xmlnametuple
in obj
.__xmlflattensequence
__.items():
347 for xmlname
in xmlnametuple
:
348 ## print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename)
349 flattenDict
[xmlname
] = sequencename
350 # handle __xmlflattensequence__ list/tuple (i.e. no element rename)
351 elif (isinstance(obj
.__xmlflattensequence
__,list) or isinstance(obj
.__xmlflattensequence
__,tuple)):
352 for sequencename
in obj
.__xmlflattensequence
__:
353 flattenDict
[sequencename
] = sequencename
355 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
357 # reattach an object's attributes to it
358 for childname
, child
in element
.children
:
359 ## print "[endElement] childname is: ", childname, "; child is: ", child
360 if flattenDict
.has_key(childname
):
361 sequencename
= _toAttrName(obj
, flattenDict
[childname
])
362 ## print "[endElement] sequencename is: ", sequencename
364 ## print "[endElement] obj.__dict__ is: ", obj.__dict__
365 sequencevalue
= obj
.__dict
__[sequencename
]
366 except (AttributeError, KeyError):
368 if sequencevalue
== None:
370 obj
.__dict
__[sequencename
] = sequencevalue
371 sequencevalue
.append(child
)
372 elif isinstance(obj
, list):
373 ## print "appended childname = ", childname
376 ## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
377 objutils
.setattrignorecase(obj
, _toAttrName(obj
, childname
), child
)
378 obj
.__dict
__[_toAttrName(obj
, childname
)] = child
381 for element
in complexType
.elements
:
383 elementName
= _toAttrName(obj
, element
.name
)
384 if ((elementName
not in obj
.__dict
__) or (obj
.__dict
__[elementName
] == None)):
385 pythonType
= xsdToPythonType(element
.type)
386 defaultValue
= _objectfactory(pythonType
, element
.default
)
387 obj
.__dict
__[elementName
] = defaultValue
389 def getRootObject(self
):
390 return self
.rootelement
392 def _toAttrName(obj
, name
):
393 if (hasattr(obj
, "__xmlrename__")):
394 for key
, val
in obj
.__xmlrename
__.iteritems():
398 ## if (name.startswith("__") and not name.endswith("__")):
399 ## name = "_%s%s" % (obj.__class__.__name__, name)
402 __typeMappingXsdToPython
= {
406 "date": "str", # ToDO Need to work out how to create python date types
408 "decimal": "float", # ToDO Does python have a better fixed point type?
414 "unicode": "unicode",
416 "duration": "str", # see above (date)
417 "datetime": "str", # see above (date)
418 "time": "str", # see above (date)
422 def xsdToPythonType(xsdType
):
424 return __typeMappingXsdToPython
[xsdType
]
426 raise Exception("Unknown xsd type %s" % xsdType
)
428 def _getXmlValue(pythonValue
):
429 if (isinstance(pythonValue
, bool)):
430 return str(pythonValue
).lower()
431 elif (isinstance(pythonValue
, unicode)):
432 return pythonValue
.encode()
434 return str(pythonValue
)
436 def unmarshal(xmlstr
, knownTypes
=None, knownModule
=None):
437 global knownGlobalTypes
, knownGlobalModule
438 if (knownTypes
== None):
439 knownGlobalTypes
= {}
441 knownGlobalTypes
= knownTypes
442 knownGlobalModule
= knownModule
443 objectfactory
= XMLObjectFactory()
444 xml
.sax
.parseString(xmlstr
, objectfactory
)
445 return objectfactory
.getRootObject()
448 def marshal(obj
, elementName
=None, prettyPrint
=False, indent
=0, knownTypes
=None, withEncoding
=True, encoding
=None):
449 xmlstr
= ''.join(_marshal(obj
, elementName
, prettyPrint
=prettyPrint
, indent
=indent
, knownTypes
=knownTypes
))
452 return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys
.getdefaultencoding(), xmlstr
)
454 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding
, xmlstr
.encode(encoding
))
458 def _marshal(obj
, elementName
=None, nameSpacePrefix
='', nameSpaces
=None, prettyPrint
=False, indent
=0, knownTypes
=None):
459 if prettyPrint
or indent
:
468 ## Determine the XML element name. If it isn't specified in the
469 ## parameter list, look for it in the __xmlname__ Python
470 ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
471 if not nameSpaces
: nameSpaces
= {} # Need to do this since if the {}
is a default parameter it gets shared by all calls into the function
473 if knownTypes
== None:
475 if hasattr(obj
, '__xmlnamespaces__'):
476 for nameSpaceKey
, nameSpaceUrl
in getattr(obj
, '__xmlnamespaces__').items():
477 if nameSpaceUrl
in nameSpaces
:
478 nameSpaceKey
= nameSpaces
[nameSpaceUrl
]
480 ## # TODO: Wait to do this until there is shared state for use when going through the object graph
481 ## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
483 ## while nameSpaceKey in nameSpaces.values():
484 ## nameSpaceKey = origNameSpaceKey + str(i)
486 nameSpaces
[nameSpaceUrl
] = nameSpaceKey
487 if nameSpaceKey
== '':
488 nameSpaceAttrs
+= ' xmlns="%s" ' % (nameSpaceUrl
)
490 nameSpaceAttrs
+= ' xmlns:%s="%s" ' % (nameSpaceKey
, nameSpaceUrl
)
491 nameSpaceAttrs
= nameSpaceAttrs
.rstrip()
492 if hasattr(obj
, '__xmldefaultnamespace__'):
493 nameSpacePrefix
= getattr(obj
, '__xmldefaultnamespace__') + ':'
495 if hasattr(obj
, '__xmlname__'):
496 elementName
= nameSpacePrefix
+ obj
.__xmlname
__
498 elementName
= nameSpacePrefix
+ BASETYPE_ELEMENT_NAME
500 elementName
= nameSpacePrefix
+ elementName
501 if hasattr(obj
, '__xmlsequencer__'):
502 elementAdd
= obj
.__xmlsequencer
__
506 ## print "marshal: entered with elementName: ", elementName
508 ## Add more members_to_skip based on ones the user has selected
509 ## via the __xmlexclude__ attribute.
510 if hasattr(obj
, '__xmlexclude__'):
511 ## print "marshal: found __xmlexclude__"
512 members_to_skip
+= list(obj
.__xmlexclude
__)
513 # Marshal the attributes that are selected to be XML attributes.
515 className
= obj
.__class
__.__name
__
516 classNamePrefix
= "_" + className
517 if hasattr(obj
, '__xmlattributes__'):
518 ## print "marshal: found __xmlattributes__"
519 xmlattributes
= obj
.__xmlattributes
__
520 members_to_skip
+= xmlattributes
521 for attr
in xmlattributes
:
522 internalAttrName
= attr
523 if (attr
.startswith("__") and not attr
.endswith("__")):
524 internalAttrName
= classNamePrefix
+ attr
525 # Fail silently if a python attribute is specified to be
526 # an XML attribute but is missing.
527 ## print "marshal: processing attribute ", internalAttrName
529 value
= obj
.__dict
__[internalAttrName
]
532 ## # But, check and see if it is a property first:
533 ## if (objutils.hasPropertyValue(obj, attr)):
534 ## value = getattr(obj, attr)
538 if hasattr(obj
, '__xsdcomplextype__'):
539 ## print "marshal: found __xsdcomplextype__"
540 complexType
= getattr(obj
, '__xsdcomplextype__')
541 xsdElement
= complexType
.findElement(attr
)
543 default
= xsdElement
.default
544 if default
== value
or default
== _getXmlValue(value
):
549 # ToDO remove maxOccurs hack after bug 177 is fixed
550 if attr
== "maxOccurs" and value
== -1:
553 if isinstance(value
, bool):
559 attrNameSpacePrefix
= ''
560 if hasattr(obj
, '__xmlattrnamespaces__'):
561 ## print "marshal: found __xmlattrnamespaces__"
562 for nameSpaceKey
, nameSpaceAttributes
in getattr(obj
, '__xmlattrnamespaces__').items():
563 if nameSpaceKey
== nameSpacePrefix
[:-1]: # Don't need to specify attribute namespace if it is the same as it's element
565 if attr
in nameSpaceAttributes
:
566 attrNameSpacePrefix
= nameSpaceKey
+ ':'
568 ## if attr.startswith('_'):
570 if (hasattr(obj
, "__xmlrename__") and attr
in obj
.__xmlrename
__):
571 ## print "marshal: found __xmlrename__ (and its attribute)"
572 attr
= obj
.__xmlrename
__[attr
]
574 objattrs
+= ' %s%s="%s"' % (attrNameSpacePrefix
, attr
, value
)
575 ## print "marshal: new objattrs is: ", objattrs
577 if isinstance(obj
, NoneType
):
579 elif isinstance(obj
, bool):
580 return ['%s<%s objtype="bool">%s</%s>%s' % (prefix
, elementName
, obj
, elementName
, newline
)]
581 elif isinstance(obj
, int):
582 return ['''%s<%s objtype="int">%s</%s>%s''' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
583 elif isinstance(obj
, long):
584 return ['%s<%s objtype="long">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
585 elif isinstance(obj
, float):
586 return ['%s<%s objtype="float">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
587 elif isinstance(obj
, unicode): # have to check before basestring - unicode is instance of base string
588 return ['''%s<%s>%s</%s>%s''' % (prefix
, elementName
, saxutils
.escape(obj
.encode()), elementName
, newline
)]
589 elif isinstance(obj
, basestring
):
590 return ['''%s<%s>%s</%s>%s''' % (prefix
, elementName
, saxutils
.escape(obj
), elementName
, newline
)]
591 elif isinstance(obj
, list):
594 xmlString
= ['%s<%s objtype="list">%s' % (prefix
, elementName
, newline
)]
596 xmlString
.extend(_marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
597 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
599 elif isinstance(obj
, tuple):
602 xmlString
= ['%s<%s objtype="list" mutable="false">%s' % (prefix
, elementName
, newline
)]
604 xmlString
.extend(_marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
605 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
607 elif isinstance(obj
, dict):
608 xmlString
= ['%s<%s objtype="dict">%s' % (prefix
, elementName
, newline
)]
609 subprefix
= prefix
+ ' '*increment
610 subindent
= indent
+ 2*increment
611 for key
, val
in obj
.iteritems():
612 xmlString
.append("%s<key>%s" % (subprefix
, newline
))
613 xmlString
.extend(_marshal(key
, indent
=subindent
, knownTypes
=knownTypes
))
614 xmlString
.append("%s</key>%s%s<value>%s" % (subprefix
, newline
, subprefix
, newline
))
615 xmlString
.extend(_marshal(val
, nameSpaces
=nameSpaces
, indent
=subindent
, knownTypes
=knownTypes
))
616 xmlString
.append("%s</value>%s" % (subprefix
, newline
))
617 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
620 moduleName
= obj
.__class
__.__module
__
621 if (moduleName
== "activegrid.model.schema"):
622 xmlString
= ['%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)]
624 # Only add the objtype if the element tag is unknown to us.
626 objname
= knownTypes
[elementName
]
627 xmlString
= ['%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)]
629 xmlString
= ['%s<%s%s%s objtype="%s.%s"' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
, moduleName
, className
)]
630 ## print "UnknownTypeException: Unknown type (%s.%s) passed to marshaller" % (moduleName, className)
631 # get the member, value pairs for the object, filtering out the types we don't support
632 if (elementAdd
!= None):
633 prefix
+= increment
*' '
637 if hasattr(obj
, '__xmlbody__'):
638 xmlbody
= getattr(obj
, obj
.__xmlbody
__)
640 xmlMemberString
.append(xmlbody
)
642 entryList
= obj
.__dict
__.items()
643 ## # Add in properties
644 ## for key in obj.__class__.__dict__.iterkeys():
645 ## if (key not in members_to_skip and key not in obj.__dict__
646 ## and objutils.hasPropertyValue(obj, key)):
647 ## value = getattr(obj, key)
648 ## entryList.append((key, value))
650 if hasattr(obj
, '__xmlattrgroups__'):
651 attrGroups
= obj
.__xmlattrgroups
__
652 if (not isinstance(attrGroups
,dict)):
653 raise "__xmlattrgroups__ is not a dict, but must be"
659 # add the list of all attributes to attrGroups
661 for x
, z
in entryList
:
663 attrGroups
['__nogroup__'] = eList
665 for eName
in attrGroups
:
666 eList
= attrGroups
[eName
]
667 if (eName
!= '__nogroup__'):
668 prefix
+= increment
*' '
670 xmlMemberString
.append('%s<%s objtype="None">%s' % (prefix
, eName
, newline
))
672 value
= obj
.__dict
__[name
]
673 ## print " ", name, " = ", value
674 ## # special name handling for private "__*" attributes:
675 ## # remove the _<class-name> added by Python
676 ## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
677 if eName
== '__nogroup__' and name
in members_to_skip
: continue
678 if name
.startswith('__') and name
.endswith('__'): continue
679 ## idx = name.find('__')
681 ## newName = name[idx+2:]
684 ## print "marshal: processing subElement ", name
685 subElementNameSpacePrefix
= nameSpacePrefix
686 if hasattr(obj
, '__xmlattrnamespaces__'):
687 for nameSpaceKey
, nameSpaceValues
in getattr(obj
, '__xmlattrnamespaces__').items():
688 if name
in nameSpaceValues
:
689 subElementNameSpacePrefix
= nameSpaceKey
+ ':'
691 # handle sequences listed in __xmlflattensequence__
692 # specially: instead of listing the contained items inside
693 # of a separate list, as god intended, list them inside
694 # the object containing the sequence.
695 if hasattr(obj
, '__xmlflattensequence__') and name
in obj
.__xmlflattensequence
__ and value
:
697 xmlnametuple
= obj
.__xmlflattensequence
__[name
]
699 if len(xmlnametuple
) == 1:
700 xmlname
= xmlnametuple
[0]
703 ## xmlname = name.lower()
704 for seqitem
in value
:
705 xmlMemberString
.extend(_marshal(seqitem
, xmlname
, subElementNameSpacePrefix
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
707 if (hasattr(obj
, "__xmlrename__") and name
in obj
.__xmlrename
__):
708 xmlname
= obj
.__xmlrename
__[name
]
712 ## print "getting pretty deep, xmlname = ", xmlname
713 xmlMemberString
.extend(_marshal(value
, xmlname
, subElementNameSpacePrefix
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
714 if (eName
!= '__nogroup__'):
715 ## print "marshal: Completing attrGroup ", eName
716 xmlMemberString
.append('%s</%s>%s' % (prefix
, eName
, newline
))
717 prefix
= prefix
[:-increment
]
720 # if we have nested elements, add them here, otherwise close the element tag immediately.
721 xmlMemberString
= filter(lambda x
: len(x
)>0, xmlMemberString
)
722 if len(xmlMemberString
) > 0:
723 xmlString
.append('>')
724 if hasattr(obj
, '__xmlbody__'):
725 xmlString
.extend(xmlMemberString
)
726 xmlString
.append('</%s>%s' % (elementName
, newline
))
728 xmlString
.append(newline
)
729 if (elementAdd
!= None):
730 xmlString
.append('%s<%s>%s' % (prefix
, elementAdd
, newline
))
731 xmlString
.extend(xmlMemberString
)
732 if (elementAdd
!= None):
733 xmlString
.append('%s</%s>%s' % (prefix
, elementAdd
, newline
))
734 prefix
= prefix
[:-increment
]
736 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
738 xmlString
.append('/>%s' % newline
)