]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/util/xmlmarshaller.py
More updates to the docview library modules and sample apps from the
[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 else:
199 if modulename:
200 module = __import__(modulename)
201 for name in pathlist[1:-1]:
202 module = module.__dict__[name]
203 if objargs:
204 return module.__dict__[objtype](*objargs)
205 else:
206 if objtype == 'None':
207 return None
208 return module.__dict__[objtype]()
209 except KeyError:
210 raise KeyError("Could not find class %s" % objname)
211
212 class Element:
213 def __init__(self, name, attrs=None):
214 self.name = name
215 self.attrs = attrs
216 self.content = ''
217 self.children = []
218 def getobjtype(self):
219 if self.attrs.has_key('objtype'):
220 return self.attrs.getValue('objtype')
221 else:
222 return 'str'
223 def toString(self):
224 print " name = ", self.name, "; attrs = ", self.attrs, "number of children = ", len(self.children)
225 i = -1
226 for child in self.children:
227 i = i + 1
228 childClass = child.__class__.__name__
229 print " Child ", i, " class: ",childClass
230
231
232 class XMLObjectFactory(xml.sax.ContentHandler):
233 def __init__(self):
234 self.rootelement = None
235 self.elementstack = []
236 xml.sax.handler.ContentHandler.__init__(self)
237
238 def toString(self):
239 print "-----XMLObjectFactory Dump-------------------------------"
240 if (self.rootelement == None):
241 print "rootelement is None"
242 else:
243 print "rootelement is an object"
244 i = -1
245 print "length of elementstack is: ", len(self.elementstack)
246 for e in self.elementstack:
247 i = i + 1
248 print "elementstack[", i, "]: "
249 e.toString()
250 print "-----end XMLObjectFactory--------------------------------"
251
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
262
263 def characters(self, content):
264 ## print "got content: %s" % content
265 if content:
266 self.elementstack[-1].content += content
267
268 def endElement(self, name):
269 ## print "[endElement] name of element we're at the end of: %s" % name
270 xsname = 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
284 return
285 constructorarglist = []
286 if element.content:
287 strippedElementContent = element.content.strip()
288 if strippedElementContent:
289 constructorarglist.append(element.content)
290 ## print "[endElement] calling objectfactory"
291 obj = _objectfactory(objtype, constructorarglist, xsname)
292 complexType = None
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"
297 ## self.toString()
298 self.elementstack[-1].children = oldChildren
299 ## self.toString()
300 ## print "done moving sequence stuff; returning"
301 return
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"
306 else:
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:]
314 else:
315 ns = ""
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:]
326 if complexType:
327 xsdElement = complexType.findElement(attrname)
328 if xsdElement:
329 type = xsdElement.type
330 if type:
331 type = xsdToPythonType(type)
332 ### ToDO remove maxOccurs hack after bug 177 is fixed
333 if attrname == "maxOccurs" and attr == "unbounded":
334 attr = "-1"
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__
339 flattenDict = {}
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
352 else:
353 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
354
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
361 try:
362 ## print "[endElement] obj.__dict__ is: ", obj.__dict__
363 sequencevalue = obj.__dict__[sequencename]
364 except (AttributeError, KeyError):
365 sequencevalue = None
366 if sequencevalue == None:
367 sequencevalue = []
368 obj.__dict__[sequencename] = sequencevalue
369 sequencevalue.append(child)
370 elif isinstance(obj, list):
371 ## print "appended childname = ", childname
372 obj.append(child)
373 else:
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
377
378 if complexType:
379 for element in complexType.elements:
380 if element.default:
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
386
387 def getRootObject(self):
388 return self.rootelement
389
390 def _toAttrName(obj, name):
391 if (hasattr(obj, "__xmlrename__")):
392 for key, val in obj.__xmlrename__.iteritems():
393 if (name == val):
394 name = key
395 break
396 ## if (name.startswith("__") and not name.endswith("__")):
397 ## name = "_%s%s" % (obj.__class__.__name__, name)
398 return name
399
400 __typeMappingXsdToPython = {
401 "string": "str",
402 "char": "str",
403 "varchar": "str",
404 "date": "str", # ToDO Need to work out how to create python date types
405 "boolean": "bool",
406 "decimal": "float", # ToDO Does python have a better fixed point type?
407 "int": "int",
408 "long": "long",
409 "float": "float",
410 "bool": "bool",
411 "str": "str",
412 "unicode": "unicode",
413 "short": "int",
414 "duration": "str", # see above (date)
415 "datetime": "str", # see above (date)
416 "time": "str", # see above (date)
417 "double": "float",
418 }
419
420 def xsdToPythonType(xsdType):
421 try:
422 return __typeMappingXsdToPython[xsdType]
423 except KeyError:
424 raise Exception("Unknown xsd type %s" % xsdType)
425
426 def _getXmlValue(pythonValue):
427 if (isinstance(pythonValue, bool)):
428 return str(pythonValue).lower()
429 elif (isinstance(pythonValue, unicode)):
430 return pythonValue.encode()
431 else:
432 return str(pythonValue)
433
434 def unmarshal(xmlstr, knownTypes=None):
435 global knownGlobalTypes
436 if (knownTypes == None):
437 knownGlobalTypes = {}
438 else:
439 knownGlobalTypes = knownTypes
440 objectfactory = XMLObjectFactory()
441 xml.sax.parseString(xmlstr, objectfactory)
442 return objectfactory.getRootObject()
443
444
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))
447 if withEncoding:
448 if encoding is None:
449 return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys.getdefaultencoding(), xmlstr)
450 else:
451 return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr.encode(encoding))
452 else:
453 return xmlstr
454
455 def _marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0, knownTypes=None):
456 if prettyPrint or indent:
457 prefix = ' '*indent
458 newline = '\n'
459 increment = 4
460 else:
461 prefix = ''
462 newline = ''
463 increment = 0
464
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
469 nameSpaceAttrs = ''
470 if knownTypes == None:
471 knownTypes = {}
472 if hasattr(obj, '__xmlnamespaces__'):
473 for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
474 if nameSpaceUrl in nameSpaces:
475 nameSpaceKey = nameSpaces[nameSpaceUrl]
476 else:
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
479 ## i = 1
480 ## while nameSpaceKey in nameSpaces.values():
481 ## nameSpaceKey = origNameSpaceKey + str(i)
482 ## i += 1
483 nameSpaces[nameSpaceUrl] = nameSpaceKey
484 if nameSpaceKey == '':
485 nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
486 else:
487 nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
488 nameSpaceAttrs = nameSpaceAttrs.rstrip()
489 if hasattr(obj, '__xmldefaultnamespace__'):
490 nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
491 if not elementName:
492 if hasattr(obj, '__xmlname__'):
493 elementName = nameSpacePrefix + obj.__xmlname__
494 else:
495 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
496 else:
497 elementName = nameSpacePrefix + elementName
498 if hasattr(obj, '__xmlsequencer__'):
499 elementAdd = obj.__xmlsequencer__
500 else:
501 elementAdd = None
502
503 ## print "marshal: entered with elementName: ", elementName
504 members_to_skip = []
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.
511 objattrs = ''
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
525 try:
526 value = obj.__dict__[internalAttrName]
527 except KeyError:
528 continue
529 ## # But, check and see if it is a property first:
530 ## if (objutils.hasPropertyValue(obj, attr)):
531 ## value = getattr(obj, attr)
532 ## else:
533 ## continue
534 xsdElement = None
535 if hasattr(obj, '__xsdcomplextype__'):
536 ## print "marshal: found __xsdcomplextype__"
537 complexType = getattr(obj, '__xsdcomplextype__')
538 xsdElement = complexType.findElement(attr)
539 if xsdElement:
540 default = xsdElement.default
541 if default == value or default == _getXmlValue(value):
542 continue
543 elif value == None:
544 continue
545
546 # ToDO remove maxOccurs hack after bug 177 is fixed
547 if attr == "maxOccurs" and value == -1:
548 value = "unbounded"
549
550 if isinstance(value, bool):
551 if value == True:
552 value = "true"
553 else:
554 value = "false"
555
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
561 continue
562 if attr in nameSpaceAttributes:
563 attrNameSpacePrefix = nameSpaceKey + ':'
564 break
565 ## if attr.startswith('_'):
566 ## attr = attr[1:]
567 if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
568 ## print "marshal: found __xmlrename__ (and its attribute)"
569 attr = obj.__xmlrename__[attr]
570
571 objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
572 ## print "marshal: new objattrs is: ", objattrs
573
574 if isinstance(obj, NoneType):
575 return ''
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):
589 if len(obj) < 1:
590 return ''
591 xmlString = ['%s<%s objtype="list">%s' % (prefix, elementName, newline)]
592 for item in obj:
593 xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
594 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
595 return xmlString
596 elif isinstance(obj, tuple):
597 if len(obj) < 1:
598 return ''
599 xmlString = ['%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)]
600 for item in obj:
601 xmlString.extend(_marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
602 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
603 return xmlString
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))
615 return xmlString
616 else:
617 moduleName = obj.__class__.__module__
618 if (moduleName == "activegrid.model.schema"):
619 xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)]
620 else:
621 # Only add the objtype if the element tag is unknown to us.
622 try:
623 objname = knownTypes[elementName]
624 xmlString = ['%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)]
625 except KeyError:
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*' '
631 indent += increment
632
633 xmlMemberString = []
634 if hasattr(obj, '__xmlbody__'):
635 xmlbody = getattr(obj, obj.__xmlbody__)
636 if xmlbody != None:
637 xmlMemberString.append(xmlbody)
638 else:
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))
646 entryList.sort()
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"
651 for n in attrGroups:
652 v = attrGroups[n]
653 members_to_skip += v
654 else:
655 attrGroups = {}
656 # add the list of all attributes to attrGroups
657 eList = []
658 for x, z in entryList:
659 eList.append(x)
660 attrGroups['__nogroup__'] = eList
661
662 for eName in attrGroups:
663 eList = attrGroups[eName]
664 if (eName != '__nogroup__'):
665 prefix += increment*' '
666 indent += increment
667 xmlMemberString.append('%s<%s objtype="None">%s' % (prefix, eName, newline))
668 for name in eList:
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('__')
677 ## if idx > 0:
678 ## newName = name[idx+2:]
679 ## if newName:
680 ## name = newName
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 + ':'
687 break
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:
693 try:
694 xmlnametuple = obj.__xmlflattensequence__[name]
695 xmlname = None
696 if len(xmlnametuple) == 1:
697 xmlname = xmlnametuple[0]
698 except:
699 xmlname = name
700 ## xmlname = name.lower()
701 for seqitem in value:
702 xmlMemberString.extend(_marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes))
703 else:
704 if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
705 xmlname = obj.__xmlrename__[name]
706 else:
707 xmlname = name
708 ## if (indent > 30):
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]
715 indent -= increment
716
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))
724 else:
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]
732 indent -= increment
733 xmlString.append('%s</%s>%s' % (prefix, elementName, newline))
734 else:
735 xmlString.append('/>%s' % newline)
736 return xmlString