]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/util/xmlmarshaller.py
DocView patches from Morgen Hua: bug fixes, and additional SVN
[wxWidgets.git] / wxPython / samples / ide / activegrid / util / xmlmarshaller.py
1 #----------------------------------------------------------------------------
2 # Name: xmlmarshaller.py
3 # Purpose:
4 #
5 # Author: John Spurling
6 #
7 # Created: 7/28/04
8 # CVS-ID: $Id$
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12 import __builtin__
13 import sys
14 from types import *
15 import xml.sax
16 import xml.sax.handler
17 from xml.sax import saxutils
18
19 import objutils
20
21 MODULE_PATH = "__main__"
22
23 ### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
24
25 """
26
27 More documentation later, but here are some special Python attributes
28 that McLane recognizes:
29
30 name: __xmlname__
31 type: string
32 description: the name of the xml element for the marshalled object
33
34 name: __xmlattributes__
35 type: tuple or list
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.
40
41 name: __xmlexclude__
42 type: tuple or list
43 description: the name(s) of the python attribute(s) to skip when
44 marshalling.
45
46 name: __xmlrename__
47 type: dict
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
51 associated XML names.
52
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.:
59
60 myseq = (1, 2)
61 <!-- normal way of marshalling -->
62 <myseq>
63 <item objtype='int'>1</item>
64 <item objtype='int'>2</item>
65 </myseq>
66 <!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
67 <squish objtype='int'>1</squish>
68 <squish objtype='int'>2</squish>
69
70 name: __xmlnamespaces__
71 type: dict
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.:
75
76 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
77
78 name: __xmldefaultnamespace__
79 type: String
80 description: the prefix of a namespace defined in __xmlnamespaces__ that
81 should be used as the default namespace for the object.
82
83 name: __xmlattrnamespaces__
84 type: dict
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.:
89
90 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
91
92 name: __xmlattrgroups__
93 type: dict
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
96 within it. e.g.
97
98 __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
99
100 """
101
102 ################################################################################
103 #
104 # module exceptions
105 #
106 ################################################################################
107
108 class Error(Exception):
109 """Base class for errors in this module."""
110 pass
111
112 class UnhandledTypeException(Error):
113 """Exception raised when attempting to marshal an unsupported
114 type.
115 """
116 def __init__(self, typename):
117 self.typename = typename
118 def __str__(self):
119 return "%s is not supported for marshalling." % str(self.typename)
120
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
124 a nested element.
125 """
126 def __init__(self, attrname, typename):
127 self.attrname = attrname
128 self.typename = typename
129 def __str__(self):
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)
133
134 ################################################################################
135 #
136 # constants and such
137 #
138 ################################################################################
139
140 XMLNS = 'xmlns'
141 XMLNS_PREFIX = XMLNS + ':'
142 XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
143
144 BASETYPE_ELEMENT_NAME = 'item'
145
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__',
151 '__xmlattrgroups__')
152
153 ################################################################################
154 #
155 # classes and functions
156 #
157 ################################################################################
158
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.
162 '''
163 # split the objname into the typename and module path,
164 # importing the module if need be.
165 if not isinstance(objargs, list):
166 objargs = [objargs]
167
168 if (xsname):
169 try:
170 objname = knownGlobalTypes[xsname]
171 except KeyError:
172 pass
173
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])
178
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 !!!
183 if len(objargs) > 0:
184 return saxutils.unescape(objargs[0]).encode()
185 else:
186 return ''
187 elif objname == 'unicode': # don't strip strings - blanks are significant !!!
188 if len(objargs) > 0:
189 return saxutils.unescape(objargs[0]).encode()
190 else:
191 return ''
192 elif objtype in ('float', 'int', 'str', 'long'):
193 objargs = [x.strip() for x in objargs]
194
195 try:
196 if __builtin__.__dict__.has_key(objname):
197 module = __builtin__
198 elif knownGlobalModule:
199 module = knownGlobalModule
200 else:
201 if modulename:
202 module = __import__(modulename)
203 for name in pathlist[1:-1]:
204 module = module.__dict__[name]
205 if objargs:
206 return module.__dict__[objtype](*objargs)
207 else:
208 if objtype == 'None':
209 return None
210 return module.__dict__[objtype]()
211 except KeyError:
212 raise KeyError("Could not find class %s" % objname)
213
214 class Element:
215 def __init__(self, name, attrs=None):
216 self.name = name
217 self.attrs = attrs
218 self.content = ''
219 self.children = []
220 def getobjtype(self):
221 if self.attrs.has_key('objtype'):
222 return self.attrs.getValue('objtype')
223 else:
224 return 'str'
225 def toString(self):
226 print " name = ", self.name, "; attrs = ", self.attrs, "number of children = ", len(self.children)
227 i = -1
228 for child in self.children:
229 i = i + 1
230 childClass = child.__class__.__name__
231 print " Child ", i, " class: ",childClass
232
233
234 class XMLObjectFactory(xml.sax.ContentHandler):
235 def __init__(self):
236 self.rootelement = None
237 self.elementstack = []
238 xml.sax.handler.ContentHandler.__init__(self)
239
240 def toString(self):
241 print "-----XMLObjectFactory Dump-------------------------------"
242 if (self.rootelement == None):
243 print "rootelement is None"
244 else:
245 print "rootelement is an object"
246 i = -1
247 print "length of elementstack is: ", len(self.elementstack)
248 for e in self.elementstack:
249 i = i + 1
250 print "elementstack[", i, "]: "
251 e.toString()
252 print "-----end XMLObjectFactory--------------------------------"
253
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
264
265 def characters(self, content):
266 ## print "got content: %s" % content
267 if content:
268 self.elementstack[-1].content += content
269
270 def endElement(self, name):
271 ## print "[endElement] name of element we're at the end of: %s" % name
272 xsname = 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
286 return
287 constructorarglist = []
288 if element.content:
289 strippedElementContent = element.content.strip()
290 if strippedElementContent:
291 constructorarglist.append(element.content)
292 ## print "[endElement] calling objectfactory"
293 obj = _objectfactory(objtype, constructorarglist, xsname)
294 complexType = None
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"
299 ## self.toString()
300 self.elementstack[-1].children = oldChildren
301 ## self.toString()
302 ## print "done moving sequence stuff; returning"
303 return
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"
308 else:
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:]
316 else:
317 ns = ""
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:]
328 if complexType:
329 xsdElement = complexType.findElement(attrname)
330 if xsdElement:
331 type = xsdElement.type
332 if type:
333 type = xsdToPythonType(type)
334 ### ToDO remove maxOccurs hack after bug 177 is fixed
335 if attrname == "maxOccurs" and attr == "unbounded":
336 attr = "-1"
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__
341 flattenDict = {}
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
354 else:
355 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
356
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
363 try:
364 ## print "[endElement] obj.__dict__ is: ", obj.__dict__
365 sequencevalue = obj.__dict__[sequencename]
366 except (AttributeError, KeyError):
367 sequencevalue = None
368 if sequencevalue == None:
369 sequencevalue = []
370 obj.__dict__[sequencename] = sequencevalue
371 sequencevalue.append(child)
372 elif isinstance(obj, list):
373 ## print "appended childname = ", childname
374 obj.append(child)
375 else:
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
379
380 if complexType:
381 for element in complexType.elements:
382 if element.default:
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
388
389 def getRootObject(self):
390 return self.rootelement
391
392 def _toAttrName(obj, name):
393 if (hasattr(obj, "__xmlrename__")):
394 for key, val in obj.__xmlrename__.iteritems():
395 if (name == val):
396 name = key
397 break
398 ## if (name.startswith("__") and not name.endswith("__")):
399 ## name = "_%s%s" % (obj.__class__.__name__, name)
400 return name
401
402 __typeMappingXsdToPython = {
403 "string": "str",
404 "char": "str",
405 "varchar": "str",
406 "date": "str", # ToDO Need to work out how to create python date types
407 "boolean": "bool",
408 "decimal": "float", # ToDO Does python have a better fixed point type?
409 "int": "int",
410 "long": "long",
411 "float": "float",
412 "bool": "bool",
413 "str": "str",
414 "unicode": "unicode",
415 "short": "int",
416 "duration": "str", # see above (date)
417 "datetime": "str", # see above (date)
418 "time": "str", # see above (date)
419 "double": "float",
420 }
421
422 def xsdToPythonType(xsdType):
423 try:
424 return __typeMappingXsdToPython[xsdType]
425 except KeyError:
426 raise Exception("Unknown xsd type %s" % xsdType)
427
428 def _getXmlValue(pythonValue):
429 if (isinstance(pythonValue, bool)):
430 return str(pythonValue).lower()
431 elif (isinstance(pythonValue, unicode)):
432 return pythonValue.encode()
433 else:
434 return str(pythonValue)
435
436 def unmarshal(xmlstr, knownTypes=None, knownModule=None):
437 global knownGlobalTypes, knownGlobalModule
438 if (knownTypes == None):
439 knownGlobalTypes = {}
440 else:
441 knownGlobalTypes = knownTypes
442 knownGlobalModule = knownModule
443 objectfactory = XMLObjectFactory()
444 xml.sax.parseString(xmlstr, objectfactory)
445 return objectfactory.getRootObject()
446
447
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))
450 if withEncoding:
451 if encoding is None:
452 return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys.getdefaultencoding(), xmlstr)
453 else:
454 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr.encode(encoding))
455 else:
456 return xmlstr
457
458 def _marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0, knownTypes=None):
459 if prettyPrint or indent:
460 prefix = ' '*indent
461 newline = '\n'
462 increment = 4
463 else:
464 prefix = ''
465 newline = ''
466 increment = 0
467
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
472 nameSpaceAttrs = ''
473 if knownTypes == None:
474 knownTypes = {}
475 if hasattr(obj, '__xmlnamespaces__'):
476 for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
477 if nameSpaceUrl in nameSpaces:
478 nameSpaceKey = nameSpaces[nameSpaceUrl]
479 else:
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
482 ## i = 1
483 ## while nameSpaceKey in nameSpaces.values():
484 ## nameSpaceKey = origNameSpaceKey + str(i)
485 ## i += 1
486 nameSpaces[nameSpaceUrl] = nameSpaceKey
487 if nameSpaceKey == '':
488 nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
489 else:
490 nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
491 nameSpaceAttrs = nameSpaceAttrs.rstrip()
492 if hasattr(obj, '__xmldefaultnamespace__'):
493 nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
494 if not elementName:
495 if hasattr(obj, '__xmlname__'):
496 elementName = nameSpacePrefix + obj.__xmlname__
497 else:
498 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
499 else:
500 elementName = nameSpacePrefix + elementName
501 if hasattr(obj, '__xmlsequencer__'):
502 elementAdd = obj.__xmlsequencer__
503 else:
504 elementAdd = None
505
506 ## print "marshal: entered with elementName: ", elementName
507 members_to_skip = []
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.
514 objattrs = ''
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
528 try:
529 value = obj.__dict__[internalAttrName]
530 except KeyError:
531 continue
532 ## # But, check and see if it is a property first:
533 ## if (objutils.hasPropertyValue(obj, attr)):
534 ## value = getattr(obj, attr)
535 ## else:
536 ## continue
537 xsdElement = None
538 if hasattr(obj, '__xsdcomplextype__'):
539 ## print "marshal: found __xsdcomplextype__"
540 complexType = getattr(obj, '__xsdcomplextype__')
541 xsdElement = complexType.findElement(attr)
542 if xsdElement:
543 default = xsdElement.default
544 if default == value or default == _getXmlValue(value):
545 continue
546 elif value == None:
547 continue
548
549 # ToDO remove maxOccurs hack after bug 177 is fixed
550 if attr == "maxOccurs" and value == -1:
551 value = "unbounded"
552
553 if isinstance(value, bool):
554 if value == True:
555 value = "true"
556 else:
557 value = "false"
558
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
564 continue
565 if attr in nameSpaceAttributes:
566 attrNameSpacePrefix = nameSpaceKey + ':'
567 break
568 ## if attr.startswith('_'):
569 ## attr = attr[1:]
570 if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
571 ## print "marshal: found __xmlrename__ (and its attribute)"
572 attr = obj.__xmlrename__[attr]
573
574 objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
575 ## print "marshal: new objattrs is: ", objattrs
576
577 if isinstance(obj, NoneType):
578 return ''
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):
592 if len(obj) < 1:
593 return ''
594 xmlString = ['%s<%s objtype="list">%s' % (prefix, elementName, newline)]
595 for item in obj:
596 xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
597 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
598 return xmlString
599 elif isinstance(obj, tuple):
600 if len(obj) < 1:
601 return ''
602 xmlString = ['%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)]
603 for item in obj:
604 xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
605 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
606 return xmlString
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))
618 return xmlString
619 else:
620 moduleName = obj.__class__.__module__
621 if (moduleName == "activegrid.model.schema"):
622 xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)]
623 else:
624 # Only add the objtype if the element tag is unknown to us.
625 try:
626 objname = knownTypes[elementName]
627 xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)]
628 except KeyError:
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*' '
634 indent += increment
635
636 xmlMemberString = []
637 if hasattr(obj, '__xmlbody__'):
638 xmlbody = getattr(obj, obj.__xmlbody__)
639 if xmlbody != None:
640 xmlMemberString.append(xmlbody)
641 else:
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))
649 entryList.sort()
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"
654 for n in attrGroups:
655 v = attrGroups[n]
656 members_to_skip += v
657 else:
658 attrGroups = {}
659 # add the list of all attributes to attrGroups
660 eList = []
661 for x, z in entryList:
662 eList.append(x)
663 attrGroups['__nogroup__'] = eList
664
665 for eName in attrGroups:
666 eList = attrGroups[eName]
667 if (eName != '__nogroup__'):
668 prefix += increment*' '
669 indent += increment
670 xmlMemberString.append('%s<%s objtype="None">%s' % (prefix, eName, newline))
671 for name in eList:
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('__')
680 ## if idx > 0:
681 ## newName = name[idx+2:]
682 ## if newName:
683 ## name = newName
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 + ':'
690 break
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:
696 try:
697 xmlnametuple = obj.__xmlflattensequence__[name]
698 xmlname = None
699 if len(xmlnametuple) == 1:
700 xmlname = xmlnametuple[0]
701 except:
702 xmlname = name
703 ## xmlname = name.lower()
704 for seqitem in value:
705 xmlMemberString.extend(_marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
706 else:
707 if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
708 xmlname = obj.__xmlrename__[name]
709 else:
710 xmlname = name
711 ## if (indent > 30):
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]
718 indent -= increment
719
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))
727 else:
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]
735 indent -= increment
736 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
737 else:
738 xmlString.append('/>%s' % newline)
739 return xmlString