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
):
200 module
= __import__(modulename
)
201 for name
in pathlist
[1:-1]:
202 module
= module
.__dict
__[name
]
204 return module
.__dict
__[objtype
](*objargs
)
206 if objtype
== 'None':
208 return module
.__dict
__[objtype
]()
210 raise KeyError("Could not find class %s" % objname
)
213 def __init__(self
, name
, attrs
=None):
218 def getobjtype(self
):
219 if self
.attrs
.has_key('objtype'):
220 return self
.attrs
.getValue('objtype')
224 print " name = ", self
.name
, "; attrs = ", self
.attrs
, "number of children = ", len(self
.children
)
226 for child
in self
.children
:
228 childClass
= child
.__class
__.__name
__
229 print " Child ", i
, " class: ",childClass
232 class XMLObjectFactory(xml
.sax
.ContentHandler
):
234 self
.rootelement
= None
235 self
.elementstack
= []
236 xml
.sax
.handler
.ContentHandler
.__init
__(self
)
239 print "-----XMLObjectFactory Dump-------------------------------"
240 if (self
.rootelement
== None):
241 print "rootelement is None"
243 print "rootelement is an object"
245 print "length of elementstack is: ", len(self
.elementstack
)
246 for e
in self
.elementstack
:
248 print "elementstack[", i
, "]: "
250 print "-----end XMLObjectFactory--------------------------------"
252 ## ContentHandler methods
253 def startElement(self
, name
, attrs
):
254 ## print "startElement for name: ", name
255 if name
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
256 name
= name
[name
.index(':') + 1:]
257 ## for attrname in attrs.getNames():
258 ## print "%s: %s" % (attrname, attrs.getValue(attrname))
259 element
= Element(name
, attrs
.copy())
260 self
.elementstack
.append(element
)
261 ## print self.elementstack
263 def characters(self
, content
):
264 ## print "got content: %s" % content
266 self
.elementstack
[-1].content
+= content
268 def endElement(self
, name
):
269 ## print "[endElement] name of element we're at the end of: %s" % name
271 if name
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
272 name
= name
[name
.index(':') + 1:]
273 oldChildren
= self
.elementstack
[-1].children
274 element
= self
.elementstack
.pop()
275 if ((len(self
.elementstack
) > 1) and (self
.elementstack
[-1].getobjtype() == "None")):
276 parentElement
= self
.elementstack
[-2]
277 ## print "[endElement] %s: found parent with objtype==None: using its grandparent" % name
278 elif (len(self
.elementstack
) > 0):
279 parentElement
= self
.elementstack
[-1]
280 objtype
= element
.getobjtype()
281 ## print "element objtype is: ", objtype
282 if (objtype
== "None"):
283 ## print "[endElement] %s: skipping a (objtype==None) end tag" % name
285 constructorarglist
= []
287 strippedElementContent
= element
.content
.strip()
288 if strippedElementContent
:
289 constructorarglist
.append(element
.content
)
290 ## print "[endElement] calling objectfactory"
291 obj
= _objectfactory(objtype
, constructorarglist
, xsname
)
293 if hasattr(obj
, '__xsdcomplextype__'):
294 complexType
= getattr(obj
, '__xsdcomplextype__')
295 if (hasattr(obj
, '__xmlname__') and getattr(obj
, '__xmlname__') == "sequence"):
296 ## print "[endElement] sequence found"
298 self
.elementstack
[-1].children
= oldChildren
300 ## print "done moving sequence stuff; returning"
302 if len(self
.elementstack
) > 0:
303 ## print "[endElement] appending child with name: ", name, "; objtype: ", objtype
304 parentElement
.children
.append((name
, obj
))
305 ## print "parentElement now has ", len(parentElement.children), " children"
307 self
.rootelement
= obj
308 if element
.attrs
and not isinstance(obj
, list):
309 ## print "[endElement] %s: element has attrs and the obj is not a list" % name
310 for attrname
, attr
in element
.attrs
.items():
311 if attrname
== XMLNS
or attrname
.startswith(XMLNS_PREFIX
):
312 if attrname
.startswith(XMLNS_PREFIX
):
313 ns
= attrname
[XMLNS_PREFIX_LENGTH
:]
316 if not hasattr(obj
, '__xmlnamespaces__'):
317 obj
.__xmlnamespaces
__ = {ns:attr}
318 elif ns
not in obj
.__xmlnamespaces
__:
319 if (hasattr(obj
.__class
__, '__xmlnamespaces__')
320 and obj
.__xmlnamespaces
__ is obj
.__class
__.__xmlnamespaces
__):
321 obj
.__xmlnamespaces
__ = dict(obj
.__xmlnamespaces
__)
322 obj
.__xmlnamespaces
__[ns
] = attr
323 elif not attrname
== 'objtype':
324 if attrname
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
325 attrname
= attrname
[attrname
.index(':') + 1:]
327 xsdElement
= complexType
.findElement(attrname
)
329 type = xsdElement
.type
331 type = xsdToPythonType(type)
332 ### ToDO remove maxOccurs hack after bug 177 is fixed
333 if attrname
== "maxOccurs" and attr
== "unbounded":
335 attr
= _objectfactory(type, attr
)
336 objutils
.setattrignorecase(obj
, _toAttrName(obj
, attrname
), attr
)
337 ## obj.__dict__[_toAttrName(obj, attrname)] = attr
338 # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
340 if hasattr(obj
, '__xmlflattensequence__'):
341 ## print "[endElement] %s: obj has __xmlflattensequence__" % name
342 if (isinstance(obj
.__xmlflattensequence
__,dict)):
343 ## print "[endElement] dict with obj.__xmlflattensequence__.items: ", obj.__xmlflattensequence__.items()
344 for sequencename
, xmlnametuple
in obj
.__xmlflattensequence
__.items():
345 for xmlname
in xmlnametuple
:
346 ## print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename)
347 flattenDict
[xmlname
] = sequencename
348 # handle __xmlflattensequence__ list/tuple (i.e. no element rename)
349 elif (isinstance(obj
.__xmlflattensequence
__,list) or isinstance(obj
.__xmlflattensequence
__,tuple)):
350 for sequencename
in obj
.__xmlflattensequence
__:
351 flattenDict
[sequencename
] = sequencename
353 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
355 # reattach an object's attributes to it
356 for childname
, child
in element
.children
:
357 ## print "[endElement] childname is: ", childname, "; child is: ", child
358 if flattenDict
.has_key(childname
):
359 sequencename
= _toAttrName(obj
, flattenDict
[childname
])
360 ## print "[endElement] sequencename is: ", sequencename
362 ## print "[endElement] obj.__dict__ is: ", obj.__dict__
363 sequencevalue
= obj
.__dict
__[sequencename
]
364 except (AttributeError, KeyError):
366 if sequencevalue
== None:
368 obj
.__dict
__[sequencename
] = sequencevalue
369 sequencevalue
.append(child
)
370 elif isinstance(obj
, list):
371 ## print "appended childname = ", childname
374 ## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
375 objutils
.setattrignorecase(obj
, _toAttrName(obj
, childname
), child
)
376 obj
.__dict
__[_toAttrName(obj
, childname
)] = child
379 for element
in complexType
.elements
:
381 elementName
= _toAttrName(obj
, element
.name
)
382 if ((elementName
not in obj
.__dict
__) or (obj
.__dict
__[elementName
] == None)):
383 pythonType
= xsdToPythonType(element
.type)
384 defaultValue
= _objectfactory(pythonType
, element
.default
)
385 obj
.__dict
__[elementName
] = defaultValue
387 def getRootObject(self
):
388 return self
.rootelement
390 def _toAttrName(obj
, name
):
391 if (hasattr(obj
, "__xmlrename__")):
392 for key
, val
in obj
.__xmlrename
__.iteritems():
396 ## if (name.startswith("__") and not name.endswith("__")):
397 ## name = "_%s%s" % (obj.__class__.__name__, name)
400 __typeMappingXsdToPython
= {
404 "date": "str", # ToDO Need to work out how to create python date types
406 "decimal": "float", # ToDO Does python have a better fixed point type?
412 "unicode": "unicode",
414 "duration": "str", # see above (date)
415 "datetime": "str", # see above (date)
416 "time": "str", # see above (date)
420 def xsdToPythonType(xsdType
):
422 return __typeMappingXsdToPython
[xsdType
]
424 raise Exception("Unknown xsd type %s" % xsdType
)
426 def _getXmlValue(pythonValue
):
427 if (isinstance(pythonValue
, bool)):
428 return str(pythonValue
).lower()
429 elif (isinstance(pythonValue
, unicode)):
430 return pythonValue
.encode()
432 return str(pythonValue
)
434 def unmarshal(xmlstr
, knownTypes
=None):
435 global knownGlobalTypes
436 if (knownTypes
== None):
437 knownGlobalTypes
= {}
439 knownGlobalTypes
= knownTypes
440 objectfactory
= XMLObjectFactory()
441 xml
.sax
.parseString(xmlstr
, objectfactory
)
442 return objectfactory
.getRootObject()
445 def marshal(obj
, elementName
=None, prettyPrint
=False, indent
=0, knownTypes
=None, withEncoding
=True, encoding
=None):
446 xmlstr
= ''.join(_marshal(obj
, elementName
, prettyPrint
=prettyPrint
, indent
=indent
, knownTypes
=knownTypes
))
449 return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys
.getdefaultencoding(), xmlstr
)
451 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding
, xmlstr
.encode(encoding
))
455 def _marshal(obj
, elementName
=None, nameSpacePrefix
='', nameSpaces
=None, prettyPrint
=False, indent
=0, knownTypes
=None):
456 if prettyPrint
or indent
:
465 ## Determine the XML element name. If it isn't specified in the
466 ## parameter list, look for it in the __xmlname__ Python
467 ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
468 if not nameSpaces
: nameSpaces
= {} # Need to do this since if the {}
is a default parameter it gets shared by all calls into the function
470 if knownTypes
== None:
472 if hasattr(obj
, '__xmlnamespaces__'):
473 for nameSpaceKey
, nameSpaceUrl
in getattr(obj
, '__xmlnamespaces__').items():
474 if nameSpaceUrl
in nameSpaces
:
475 nameSpaceKey
= nameSpaces
[nameSpaceUrl
]
477 ## # TODO: Wait to do this until there is shared state for use when going through the object graph
478 ## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
480 ## while nameSpaceKey in nameSpaces.values():
481 ## nameSpaceKey = origNameSpaceKey + str(i)
483 nameSpaces
[nameSpaceUrl
] = nameSpaceKey
484 if nameSpaceKey
== '':
485 nameSpaceAttrs
+= ' xmlns="%s" ' % (nameSpaceUrl
)
487 nameSpaceAttrs
+= ' xmlns:%s="%s" ' % (nameSpaceKey
, nameSpaceUrl
)
488 nameSpaceAttrs
= nameSpaceAttrs
.rstrip()
489 if hasattr(obj
, '__xmldefaultnamespace__'):
490 nameSpacePrefix
= getattr(obj
, '__xmldefaultnamespace__') + ':'
492 if hasattr(obj
, '__xmlname__'):
493 elementName
= nameSpacePrefix
+ obj
.__xmlname
__
495 elementName
= nameSpacePrefix
+ BASETYPE_ELEMENT_NAME
497 elementName
= nameSpacePrefix
+ elementName
498 if hasattr(obj
, '__xmlsequencer__'):
499 elementAdd
= obj
.__xmlsequencer
__
503 ## print "marshal: entered with elementName: ", elementName
505 ## Add more members_to_skip based on ones the user has selected
506 ## via the __xmlexclude__ attribute.
507 if hasattr(obj
, '__xmlexclude__'):
508 ## print "marshal: found __xmlexclude__"
509 members_to_skip
+= list(obj
.__xmlexclude
__)
510 # Marshal the attributes that are selected to be XML attributes.
512 className
= obj
.__class
__.__name
__
513 classNamePrefix
= "_" + className
514 if hasattr(obj
, '__xmlattributes__'):
515 ## print "marshal: found __xmlattributes__"
516 xmlattributes
= obj
.__xmlattributes
__
517 members_to_skip
+= xmlattributes
518 for attr
in xmlattributes
:
519 internalAttrName
= attr
520 if (attr
.startswith("__") and not attr
.endswith("__")):
521 internalAttrName
= classNamePrefix
+ attr
522 # Fail silently if a python attribute is specified to be
523 # an XML attribute but is missing.
524 ## print "marshal: processing attribute ", internalAttrName
526 value
= obj
.__dict
__[internalAttrName
]
529 ## # But, check and see if it is a property first:
530 ## if (objutils.hasPropertyValue(obj, attr)):
531 ## value = getattr(obj, attr)
535 if hasattr(obj
, '__xsdcomplextype__'):
536 ## print "marshal: found __xsdcomplextype__"
537 complexType
= getattr(obj
, '__xsdcomplextype__')
538 xsdElement
= complexType
.findElement(attr
)
540 default
= xsdElement
.default
541 if default
== value
or default
== _getXmlValue(value
):
546 # ToDO remove maxOccurs hack after bug 177 is fixed
547 if attr
== "maxOccurs" and value
== -1:
550 if isinstance(value
, bool):
556 attrNameSpacePrefix
= ''
557 if hasattr(obj
, '__xmlattrnamespaces__'):
558 ## print "marshal: found __xmlattrnamespaces__"
559 for nameSpaceKey
, nameSpaceAttributes
in getattr(obj
, '__xmlattrnamespaces__').items():
560 if nameSpaceKey
== nameSpacePrefix
[:-1]: # Don't need to specify attribute namespace if it is the same as it's element
562 if attr
in nameSpaceAttributes
:
563 attrNameSpacePrefix
= nameSpaceKey
+ ':'
565 ## if attr.startswith('_'):
567 if (hasattr(obj
, "__xmlrename__") and attr
in obj
.__xmlrename
__):
568 ## print "marshal: found __xmlrename__ (and its attribute)"
569 attr
= obj
.__xmlrename
__[attr
]
571 objattrs
+= ' %s%s="%s"' % (attrNameSpacePrefix
, attr
, value
)
572 ## print "marshal: new objattrs is: ", objattrs
574 if isinstance(obj
, NoneType
):
576 elif isinstance(obj
, bool):
577 return ['%s<%s objtype="bool">%s</%s>%s' % (prefix
, elementName
, obj
, elementName
, newline
)]
578 elif isinstance(obj
, int):
579 return ['''%s<%s objtype="int">%s</%s>%s''' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
580 elif isinstance(obj
, long):
581 return ['%s<%s objtype="long">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
582 elif isinstance(obj
, float):
583 return ['%s<%s objtype="float">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)]
584 elif isinstance(obj
, unicode): # have to check before basestring - unicode is instance of base string
585 return ['''%s<%s>%s</%s>%s''' % (prefix
, elementName
, saxutils
.escape(obj
.encode()), elementName
, newline
)]
586 elif isinstance(obj
, basestring
):
587 return ['''%s<%s>%s</%s>%s''' % (prefix
, elementName
, saxutils
.escape(obj
), elementName
, newline
)]
588 elif isinstance(obj
, list):
591 xmlString
= ['%s<%s objtype="list">%s' % (prefix
, elementName
, newline
)]
593 xmlString
.extend(_marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
594 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
596 elif isinstance(obj
, tuple):
599 xmlString
= ['%s<%s objtype="list" mutable="false">%s' % (prefix
, elementName
, newline
)]
601 xmlString
.extend(_marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
602 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
604 elif isinstance(obj
, dict):
605 xmlString
= ['%s<%s objtype="dict">%s' % (prefix
, elementName
, newline
)]
606 subprefix
= prefix
+ ' '*increment
607 subindent
= indent
+ 2*increment
608 for key
, val
in obj
.iteritems():
609 xmlString
.append("%s<key>%s" % (subprefix
, newline
))
610 xmlString
.extend(_marshal(key
, indent
=subindent
, knownTypes
=knownTypes
))
611 xmlString
.append("%s</key>%s%s<value>%s" % (subprefix
, newline
, subprefix
, newline
))
612 xmlString
.extend(_marshal(val
, nameSpaces
=nameSpaces
, indent
=subindent
, knownTypes
=knownTypes
))
613 xmlString
.append("%s</value>%s" % (subprefix
, newline
))
614 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
617 moduleName
= obj
.__class
__.__module
__
618 if (moduleName
== "activegrid.model.schema"):
619 xmlString
= ['%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)]
621 # Only add the objtype if the element tag is unknown to us.
623 objname
= knownTypes
[elementName
]
624 xmlString
= ['%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)]
626 xmlString
= ['%s<%s%s%s objtype="%s.%s"' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
, moduleName
, className
)]
627 ## print "UnknownTypeException: Unknown type (%s.%s) passed to marshaller" % (moduleName, className)
628 # get the member, value pairs for the object, filtering out the types we don't support
629 if (elementAdd
!= None):
630 prefix
+= increment
*' '
634 if hasattr(obj
, '__xmlbody__'):
635 xmlbody
= getattr(obj
, obj
.__xmlbody
__)
637 xmlMemberString
.append(xmlbody
)
639 entryList
= obj
.__dict
__.items()
640 ## # Add in properties
641 ## for key in obj.__class__.__dict__.iterkeys():
642 ## if (key not in members_to_skip and key not in obj.__dict__
643 ## and objutils.hasPropertyValue(obj, key)):
644 ## value = getattr(obj, key)
645 ## entryList.append((key, value))
647 if hasattr(obj
, '__xmlattrgroups__'):
648 attrGroups
= obj
.__xmlattrgroups
__
649 if (not isinstance(attrGroups
,dict)):
650 raise "__xmlattrgroups__ is not a dict, but must be"
656 # add the list of all attributes to attrGroups
658 for x
, z
in entryList
:
660 attrGroups
['__nogroup__'] = eList
662 for eName
in attrGroups
:
663 eList
= attrGroups
[eName
]
664 if (eName
!= '__nogroup__'):
665 prefix
+= increment
*' '
667 xmlMemberString
.append('%s<%s objtype="None">%s' % (prefix
, eName
, newline
))
669 value
= obj
.__dict
__[name
]
670 ## print " ", name, " = ", value
671 ## # special name handling for private "__*" attributes:
672 ## # remove the _<class-name> added by Python
673 ## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
674 if eName
== '__nogroup__' and name
in members_to_skip
: continue
675 if name
.startswith('__') and name
.endswith('__'): continue
676 ## idx = name.find('__')
678 ## newName = name[idx+2:]
681 ## print "marshal: processing subElement ", name
682 subElementNameSpacePrefix
= nameSpacePrefix
683 if hasattr(obj
, '__xmlattrnamespaces__'):
684 for nameSpaceKey
, nameSpaceValues
in getattr(obj
, '__xmlattrnamespaces__').items():
685 if name
in nameSpaceValues
:
686 subElementNameSpacePrefix
= nameSpaceKey
+ ':'
688 # handle sequences listed in __xmlflattensequence__
689 # specially: instead of listing the contained items inside
690 # of a separate list, as god intended, list them inside
691 # the object containing the sequence.
692 if hasattr(obj
, '__xmlflattensequence__') and name
in obj
.__xmlflattensequence
__ and value
:
694 xmlnametuple
= obj
.__xmlflattensequence
__[name
]
696 if len(xmlnametuple
) == 1:
697 xmlname
= xmlnametuple
[0]
700 ## xmlname = name.lower()
701 for seqitem
in value
:
702 xmlMemberString
.extend(_marshal(seqitem
, xmlname
, subElementNameSpacePrefix
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
704 if (hasattr(obj
, "__xmlrename__") and name
in obj
.__xmlrename
__):
705 xmlname
= obj
.__xmlrename
__[name
]
709 ## print "getting pretty deep, xmlname = ", xmlname
710 xmlMemberString
.extend(_marshal(value
, xmlname
, subElementNameSpacePrefix
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
))
711 if (eName
!= '__nogroup__'):
712 ## print "marshal: Completing attrGroup ", eName
713 xmlMemberString
.append('%s</%s>%s' % (prefix
, eName
, newline
))
714 prefix
= prefix
[:-increment
]
717 # if we have nested elements, add them here, otherwise close the element tag immediately.
718 xmlMemberString
= filter(lambda x
: len(x
)>0, xmlMemberString
)
719 if len(xmlMemberString
) > 0:
720 xmlString
.append('>')
721 if hasattr(obj
, '__xmlbody__'):
722 xmlString
.extend(xmlMemberString
)
723 xmlString
.append('</%s>%s' % (elementName
, newline
))
725 xmlString
.append(newline
)
726 if (elementAdd
!= None):
727 xmlString
.append('%s<%s>%s' % (prefix
, elementAdd
, newline
))
728 xmlString
.extend(xmlMemberString
)
729 if (elementAdd
!= None):
730 xmlString
.append('%s</%s>%s' % (prefix
, elementAdd
, newline
))
731 prefix
= prefix
[:-increment
]
733 xmlString
.append('%s</%s>%s' % (prefix
, elementName
, newline
))
735 xmlString
.append('/>%s' % newline
)