]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/ide/activegrid/util/xmlmarshaller.py
Patch from Davide Salomoni that adds an optional point
[wxWidgets.git] / wxPython / samples / ide / activegrid / util / xmlmarshaller.py
CommitLineData
1f780e48
RD
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#----------------------------------------------------------------------------
12from activegrid import util
13import inspect
14from types import *
15import xml.sax
16import xml.sax.handler
17import __builtin__
18from xml.sax import saxutils
19
20### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
21
22"""
23
24More documentation later, but here are some special Python attributes
25that McLane recognizes:
26
27name: __xmlname__
28type: string
29description: the name of the xml element for the marshalled object
30
31name: __xmlattributes__
32type: tuple or list
33description: the name(s) of the Python string attribute(s) to be
34marshalled as xml attributes instead of nested xml elements. currently
35these can only be strings since there's not a way to get the type
36information back when unmarshalling.
37
38name: __xmlexclude__
39type: tuple or list
40description: the name(s) of the python attribute(s) to skip when
41marshalling.
42
43name: __xmlrename__
44type: dict
45description: describes an alternate Python <-> XML name mapping.
46Normally the name mapping is the identity function. __xmlrename__
47overrides that. The keys are the Python names, the values are their
48associated XML names.
49
50name: __xmlflattensequence__
51type: dict, tuple, or list
52description: the name(s) of the Python sequence attribute(s) whose
53items are to be marshalled as a series of xml elements (with an
54optional keyword argument that specifies the element name to use) as
55opposed to containing them in a separate sequence element, e.g.:
56
57myseq = (1, 2)
58<!-- normal way of marshalling -->
59<myseq>
60 <item objtype='int'>1</item>
61 <item objtype='int'>2</item>
62</myseq>
63<!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
64<squish objtype='int'>1</squish>
65<squish objtype='int'>2</squish>
66
67name: __xmlnamespaces__
68type: dict
69description: a dict of the namespaces that the object uses. Each item
70in the dict should consist of a prefix,url combination where the key is
71the prefix and url is the value, e.g.:
72
73__xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
74
75name: __xmldefaultnamespace__
76type: String
77description: the prefix of a namespace defined in __xmlnamespaces__ that
78should be used as the default namespace for the object.
79
80name: __xmlattrnamespaces__
81type: dict
82description: a dict assigning the Python object's attributes to the namespaces
83defined in __xmlnamespaces__. Each item in the dict should consist of a
84prefix,attributeList combination where the key is the prefix and the value is
85a list of the Python attribute names. e.g.:
86
87__xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
88
bbf7159c
RD
89name: __xmlattrgroups__
90type: dict
91description: a dict specifying groups of attributes to be wrapped in an enclosing tag.
92The key is the name of the enclosing tag; the value is a list of attributes to include
93within it. e.g.
94
95__xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
1f780e48
RD
96
97"""
98
99################################################################################
100#
101# module exceptions
102#
103################################################################################
104
105class Error(Exception):
106 """Base class for errors in this module."""
107 pass
108
109class UnhandledTypeException(Error):
110 """Exception raised when attempting to marshal an unsupported
111 type.
112 """
113 def __init__(self, typename):
114 self.typename = typename
115 def __str__(self):
116 return "%s is not supported for marshalling." % str(self.typename)
117
118class XMLAttributeIsNotStringType(Error):
119 """Exception raised when an object's attribute is specified to be
120 marshalled as an XML attribute of the enclosing object instead of
121 a nested element.
122 """
123 def __init__(self, attrname, typename):
124 self.attrname = attrname
125 self.typename = typename
126 def __str__(self):
127 return """%s was set to be marshalled as an XML attribute
128 instead of a nested element, but the object's type is %s, not
129 string.""" % (self.attrname, self.typename)
130
131################################################################################
132#
133# constants and such
134#
135################################################################################
136
137XMLNS = 'xmlns'
138XMLNS_PREFIX = XMLNS + ':'
139XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
140
141BASETYPE_ELEMENT_NAME = 'item'
bbf7159c
RD
142
143# This list doesn't seem to be used.
144# Internal documentation or useless? You make the call!
1f780e48
RD
145MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
146 '__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
bbf7159c
RD
147 '__xmldefaultnamespace__', '__xmlattrnamespaces__',
148 '__xmlattrgroups__')
149
150#WELL_KNOWN_OBJECTS = { #"xs:element" : "activegrid.model.schema.XsdElement",
151 #"xs:complexType" : "activegrid.model.schema.XsdComplexType",
152 #"xs:sequence" : "activegrid.model.schema.XsdSequence",
153 #"xs:element" : "activegrid.model.schema.XsdElement",
154 #"xs:key" : "activegrid.model.schema.XsdKey",
155 #"xs:field" : "activegrid.model.schema.XsdKeyField",
156 #"xs:keyref" : "activegrid.model.schema.XsdKeyRef",
157 #"xs:selector" : "activegrid.model.schema.XsdKeySelector",
158 #"xs:schema" : "activegrid.model.schema.Schema",
159 #"ag:schemaOptions":"activegrid.model.schema.SchemaOptions",
160 #"ag:debug" : "activegrid.model.processmodel.DebugOperation",
161 #"ag:body" : "activegrid.model.processmodel.Body", # alan (start)
162 #"ag:cssRule" : "activegrid.model.processmodel.CssRule",
163 #"ag:datasource" : "activegrid.data.dataservice.DataSource",
164 #"ag:deployment" : "activegrid.server.deployment.Deployment",
165 #"ag:glue" : "activegrid.model.processmodel.Glue",
166 #"ag:hr" : "activegrid.model.processmodel.HorizontalRow",
167 #"ag:image" : "activegrid.model.processmodel.Image",
168 #"ag:inputs" : "activegrid.model.processmodel.Inputs",
169 #"ag:label" : "activegrid.model.processmodel.Label",
170 #"ag:processmodel" : "activegrid.model.processmodel.ProcessModel",
171 #"ag:processmodelref" : "activegrid.server.deployment.ProcessModelRef",
172 #"ag:query" : "activegrid.model.processmodel.Query",
173 #"ag:schemaref" : "activegrid.server.deployment.SchemaRef",
174 #"ag:set" : "activegrid.model.processmodel.SetOperation",
175 #"ag:text" : "activegrid.model.processmodel.Text",
176 #"ag:title" : "activegrid.model.processmodel.Title",
177 #"ag:view" : "activegrid.model.processmodel.View",
178 #"bpws:case" : "activegrid.model.processmodel.BPELCase",
179 #"bpws:invoke" : "activegrid.model.processmodel.BPELInvoke",
180 #"bpws:otherwise" : "activegrid.model.processmodel.BPELOtherwise",
181 #"bpws:process" : "activegrid.model.processmodel.BPELProcess",
182 #"bpws:reply" : "activegrid.model.processmodel.BPELReply",
183 #"bpws:switch" : "activegrid.model.processmodel.BPELSwitch",
184 #"bpws:variable" : "activegrid.model.processmodel.BPELVariable",
185 #"projectmodel" : "activegrid.tool.ProjectEditor.ProjectModel",
186 #"wsdl:message" : "activegrid.model.processmodel.WSDLMessage",
187 #"wsdl:part" : "activegrid.model.processmodel.WSDLPart",
188 #"xforms:group" : "activegrid.model.processmodel.XFormsGroup",
189 #"xforms:input" : "activegrid.model.processmodel.XFormsInput",
190 #"xforms:label" : "activegrid.model.processmodel.XFormsLabel",
191 #"xforms:output" : "activegrid.model.processmodel.XFormsOutput",
192 #"xforms:secret" : "activegrid.model.processmodel.XFormsSecret",
193 #"xforms:submit" : "activegrid.model.processmodel.XFormsSubmit"} # alan(end)
1f780e48
RD
194
195
196################################################################################
197#
198# classes and functions
199#
200################################################################################
201
202def _objectfactory(objname, objargs=None, xsname=None):
203 try:
204 '''dynamically create an object based on the objname and return
205 it. look it up in the BASETYPE_ELEMENT_MAP first.
206 '''
1f780e48
RD
207 # split the objname into the typename and module path,
208 # importing the module if need be.
209 if not isinstance(objargs, list):
210 objargs = [objargs]
211
212 if (xsname):
213 try:
bbf7159c 214 objname = knownGlobalTypes[xsname]
1f780e48
RD
215 except KeyError:
216 pass
bbf7159c
RD
217
218## print "[objectfactory] creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
1f780e48
RD
219 objtype = objname.split('.')[-1]
220 pathlist = objname.split('.')
221 modulename = '.'.join(pathlist[0:-1])
222
223## print "[objectfactory] objtype is %s" % objtype
224## print "[objectfactory] objargs is %s" % `objargs`
225
226 ## since the bool constructor will turn a string of non-zero
227 ## length into True, we call it with no argument (yielding a
228 ## False) if the string contains 'false'
229 if objtype == 'bool' and objargs[0].lower() == 'false':
230 objargs = None
231
232## if objtype == 'str':
233## print type(objargs)
234## print "string we're unescaping: '%s'" % objargs[0]
235## objargs = saxutils.unescape(objargs[0])
236 if objtype in ('float', 'int', 'str', 'long'):
237 objargs = [x.strip() for x in objargs]
238
239 if objtype == 'str':
240 objargs = [saxutils.unescape(x) for x in objargs]
241
242 if __builtin__.__dict__.has_key(objname):
243 module = __builtin__
244 else:
245 if modulename:
246 module = __import__(modulename)
247 for name in pathlist[1:-1]:
248 module = module.__dict__[name]
249 if objargs:
250 return module.__dict__[objtype](*objargs)
251 else:
252 if objtype == 'None':
253 return None
254 return module.__dict__[objtype]()
255 except KeyError:
256 raise KeyError("Could not find class %s" % objname)
257
258class Element:
259 def __init__(self, name, attrs=None):
260 self.name = name
261 self.attrs = attrs
262 self.content = ''
263 self.children = []
264 def getobjtype(self):
265 if self.attrs.has_key('objtype'):
266 return self.attrs.getValue('objtype')
267 else:
268 return 'str'
bbf7159c
RD
269 def toString(self):
270 print " name = ", self.name, "; attrs = ", self.attrs, "number of children = ", len(self.children)
271 i = -1
272 for child in self.children:
273 i = i + 1
274 childClass = child.__class__.__name__
275 print " Child ", i, " class: ",childClass
1f780e48
RD
276
277
278class XMLObjectFactory(xml.sax.ContentHandler):
279 def __init__(self):
280 self.rootelement = None
281 self.elementstack = []
282 xml.sax.handler.ContentHandler.__init__(self)
283
bbf7159c
RD
284 def toString(self):
285 print "-----XMLObjectFactory Dump-------------------------------"
286 if (self.rootelement == None):
287 print "rootelement is None"
288 else:
289 print "rootelement is an object"
290 i = -1
291 print "length of elementstack is: ", len(self.elementstack)
292 for e in self.elementstack:
293 i = i + 1
294 print "elementstack[", i, "]: "
295 e.toString()
296 print "-----end XMLObjectFactory--------------------------------"
297
1f780e48
RD
298 ## ContentHandler methods
299 def startElement(self, name, attrs):
bbf7159c 300## print "startElement for name: ", name
1f780e48
RD
301 if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
302 name = name[name.index(':') + 1:]
303## for attrname in attrs.getNames():
304## print "%s: %s" % (attrname, attrs.getValue(attrname))
305 element = Element(name, attrs.copy())
306 self.elementstack.append(element)
307## print self.elementstack
308
309 def characters(self, content):
310## print "got content: %s" % content
311 if content:
312 self.elementstack[-1].content += content
313
314 def endElement(self, name):
315## print "[endElement] name of element we're at the end of: %s" % name
316 xsname = name
317 if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
318 name = name[name.index(':') + 1:]
bbf7159c 319 oldChildren = self.elementstack[-1].children
1f780e48 320 element = self.elementstack.pop()
bbf7159c
RD
321 if ((len(self.elementstack) > 1) and (self.elementstack[-1].getobjtype() == "None")):
322 parentElement = self.elementstack[-2]
323## print "[endElement] %s: found parent with objtype==None: using its grandparent" % name
324 elif (len(self.elementstack) > 0):
325 parentElement = self.elementstack[-1]
1f780e48 326 objtype = element.getobjtype()
bbf7159c
RD
327## print "element objtype is: ", objtype
328 if (objtype == "None"):
329## print "[endElement] %s: skipping a (objtype==None) end tag" % name
330 return
1f780e48
RD
331 constructorarglist = []
332 if element.content:
333 strippedElementContent = element.content.strip()
334 if strippedElementContent:
335 constructorarglist.append(element.content)
bbf7159c 336## print "[endElement] calling objectfactory"
1f780e48
RD
337 obj = _objectfactory(objtype, constructorarglist, xsname)
338 complexType = None
339 if hasattr(obj, '__xsdcomplextype__'):
340 complexType = getattr(obj, '__xsdcomplextype__')
bbf7159c
RD
341 if (hasattr(obj, '__xmlname__') and getattr(obj, '__xmlname__') == "sequence"):
342## print "[endElement] sequence found"
343## self.toString()
344 self.elementstack[-1].children = oldChildren
345## self.toString()
346## print "done moving sequence stuff; returning"
347 return
1f780e48 348 if len(self.elementstack) > 0:
bbf7159c
RD
349## print "[endElement] appending child with name: ", name, "; objtype: ", objtype
350 parentElement.children.append((name, obj))
351## print "parentElement now has ", len(parentElement.children), " children"
1f780e48
RD
352 else:
353 self.rootelement = obj
354 if element.attrs and not isinstance(obj, list):
bbf7159c 355## print "[endElement] %s: element has attrs and the obj is not a list" % name
1f780e48
RD
356 for attrname, attr in element.attrs.items():
357 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
358 if attrname.startswith(XMLNS_PREFIX):
359 ns = attrname[XMLNS_PREFIX_LENGTH:]
360 else:
361 ns = ""
362 if not hasattr(obj, '__xmlnamespaces__'):
363 obj.__xmlnamespaces__ = {ns:attr}
364 elif ns not in obj.__xmlnamespaces__:
365 if (hasattr(obj.__class__, '__xmlnamespaces__')
366 and obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__):
367 obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__)
368 obj.__xmlnamespaces__[ns] = attr
369 elif not attrname == 'objtype':
370 if attrname.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
371 attrname = attrname[attrname.index(':') + 1:]
372 if complexType:
373 xsdElement = complexType.findElement(attrname)
374 if xsdElement:
375 type = xsdElement.type
376 if type:
377 type = xsdToPythonType(type)
378 ### ToDO remove maxOccurs hack after bug 177 is fixed
379 if attrname == "maxOccurs" and attr == "unbounded":
380 attr = "-1"
381 attr = _objectfactory(type, attr)
382 util.setattrignorecase(obj, _toAttrName(obj, attrname), attr)
383## obj.__dict__[_toAttrName(obj, attrname)] = attr
384 # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
385 flattenDict = {}
386 if hasattr(obj, '__xmlflattensequence__'):
bbf7159c
RD
387## print "[endElement] %s: obj has __xmlflattensequence__" % name
388 if (isinstance(obj.__xmlflattensequence__,dict)):
389## print "[endElement] dict with obj.__xmlflattensequence__.items: ", obj.__xmlflattensequence__.items()
390 for sequencename, xmlnametuple in obj.__xmlflattensequence__.items():
391 for xmlname in xmlnametuple:
392## print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename)
393 flattenDict[xmlname] = sequencename
394 # handle __xmlflattensequence__ list/tuple (i.e. no element rename)
395 elif (isinstance(obj.__xmlflattensequence__,list) or isinstance(obj.__xmlflattensequence__,tuple)):
396 for sequencename in obj.__xmlflattensequence__:
397 flattenDict[sequencename] = sequencename
398 else:
399 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
1f780e48
RD
400
401 # reattach an object's attributes to it
402 for childname, child in element.children:
bbf7159c 403## print "[endElement] childname is: ", childname, "; child is: ", child
1f780e48
RD
404 if flattenDict.has_key(childname):
405 sequencename = _toAttrName(obj, flattenDict[childname])
bbf7159c 406## print "[endElement] sequencename is: ", sequencename
1f780e48 407 try:
bbf7159c 408## print "[endElement] obj.__dict__ is: ", obj.__dict__
1f780e48
RD
409 sequencevalue = obj.__dict__[sequencename]
410 except AttributeError:
411 sequencevalue = None
bbf7159c
RD
412 except KeyError:
413 sequencevalue = None
1f780e48
RD
414 if sequencevalue == None:
415 sequencevalue = []
416 obj.__dict__[sequencename] = sequencevalue
417 sequencevalue.append(child)
418 elif isinstance(obj, list):
bbf7159c 419## print "appended childname = ", childname
1f780e48
RD
420 obj.append(child)
421 else:
422## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
423 util.setattrignorecase(obj, _toAttrName(obj, childname), child)
bbf7159c 424 obj.__dict__[_toAttrName(obj, childname)] = child
1f780e48
RD
425
426 if complexType:
427 for element in complexType.elements:
428 if element.default:
429 elementName = _toAttrName(obj, element.name)
430 if ((elementName not in obj.__dict__) or (obj.__dict__[elementName] == None)):
431 pythonType = xsdToPythonType(element.type)
432 defaultValue = _objectfactory(pythonType, element.default)
433 obj.__dict__[elementName] = defaultValue
434
435 def getRootObject(self):
436 return self.rootelement
437
438def _toAttrName(obj, name):
439 if (hasattr(obj, "__xmlrename__")):
440 for key, val in obj.__xmlrename__.iteritems():
441 if (name == val):
442 name = key
443 break
444## if (name.startswith("__") and not name.endswith("__")):
445## name = "_%s%s" % (obj.__class__.__name__, name)
446 return name
447
448__typeMappingXsdToPython = {
449 "string": "str",
450 "char": "str",
451 "varchar": "str",
452 "date": "str", # ToDO Need to work out how to create python date types
453 "boolean": "bool",
454 "decimal": "float", # ToDO Does python have a better fixed point type?
455 "int": "int",
456 "long": "long",
457 "float": "float",
458 "bool": "bool",
459 "str": "str",
460 "unicode": "unicode",
bbf7159c
RD
461 "short": "int",
462 "duration": "str", # see above (date)
463 "datetime": "str", # see above (date)
464 "time": "str", # see above (date)
465 "double": "float",
1f780e48
RD
466 }
467
468def xsdToPythonType(xsdType):
469 try:
470 return __typeMappingXsdToPython[xsdType]
471 except KeyError:
472 raise Exception("Unknown xsd type %s" % xsdType)
473
474def _getXmlValue(pythonValue):
475 if (isinstance(pythonValue, bool)):
476 return str(pythonValue).lower()
477 else:
478 return str(pythonValue)
479
bbf7159c
RD
480def unmarshal(xmlstr, knownTypes=None):
481 global knownGlobalTypes
482 if (knownTypes == None):
483 knownGlobalTypes = {}
484 else:
485 knownGlobalTypes = knownTypes
1f780e48
RD
486 objectfactory = XMLObjectFactory()
487 xml.sax.parseString(xmlstr, objectfactory)
488 return objectfactory.getRootObject()
489
490
bbf7159c 491def marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0, knownTypes=None):
1f780e48
RD
492 if prettyPrint or indent:
493 prefix = ' '*indent
494 newline = '\n'
495 increment = 4
496 else:
497 prefix = ''
498 newline = ''
499 increment = 0
500
501 ## Determine the XML element name. If it isn't specified in the
502 ## parameter list, look for it in the __xmlname__ Python
503 ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
504 if not nameSpaces: nameSpaces = {} # Need to do this since if the {} is a default parameter it gets shared by all calls into the function
505 nameSpaceAttrs = ''
bbf7159c
RD
506 if knownTypes == None:
507 knownTypes = {}
1f780e48
RD
508 if hasattr(obj, '__xmlnamespaces__'):
509 for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
510 if nameSpaceUrl in nameSpaces:
511 nameSpaceKey = nameSpaces[nameSpaceUrl]
512 else:
513## # TODO: Wait to do this until there is shared state for use when going through the object graph
514## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
515## i = 1
516## while nameSpaceKey in nameSpaces.values():
517## nameSpaceKey = origNameSpaceKey + str(i)
518## i += 1
519 nameSpaces[nameSpaceUrl] = nameSpaceKey
520 if nameSpaceKey == '':
521 nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
522 else:
523 nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
524 nameSpaceAttrs = nameSpaceAttrs.rstrip()
525 if hasattr(obj, '__xmldefaultnamespace__'):
bbf7159c 526 nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
1f780e48
RD
527 if not elementName:
528 if hasattr(obj, '__xmlname__'):
529 elementName = nameSpacePrefix + obj.__xmlname__
530 else:
531 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
532 else:
533 elementName = nameSpacePrefix + elementName
bbf7159c
RD
534 if hasattr(obj, '__xmlsequencer__'):
535 elementAdd = obj.__xmlsequencer__
536 else:
537 elementAdd = None
538
539## print "marshal: entered with elementName: ", elementName
1f780e48
RD
540 members_to_skip = []
541 ## Add more members_to_skip based on ones the user has selected
542 ## via the __xmlexclude__ attribute.
543 if hasattr(obj, '__xmlexclude__'):
bbf7159c 544## print "marshal: found __xmlexclude__"
1f780e48
RD
545 members_to_skip += list(obj.__xmlexclude__)
546 # Marshal the attributes that are selected to be XML attributes.
547 objattrs = ''
548 className = obj.__class__.__name__
549 classNamePrefix = "_" + className
550 if hasattr(obj, '__xmlattributes__'):
bbf7159c 551## print "marshal: found __xmlattributes__"
1f780e48
RD
552 xmlattributes = obj.__xmlattributes__
553 members_to_skip += xmlattributes
554 for attr in xmlattributes:
555 internalAttrName = attr
556 if (attr.startswith("__") and not attr.endswith("__")):
557 internalAttrName = classNamePrefix + attr
558 # Fail silently if a python attribute is specified to be
559 # an XML attribute but is missing.
bbf7159c 560## print "marshal: processing attribute ", internalAttrName
1f780e48
RD
561 try:
562 value = obj.__dict__[internalAttrName]
563 except KeyError:
564 continue
565## # But, check and see if it is a property first:
566## if (hasPropertyValue(obj, attr)):
567## value = getattr(obj, attr)
568## else:
569## continue
570 xsdElement = None
571 if hasattr(obj, '__xsdcomplextype__'):
bbf7159c 572## print "marshal: found __xsdcomplextype__"
1f780e48
RD
573 complexType = getattr(obj, '__xsdcomplextype__')
574 xsdElement = complexType.findElement(attr)
575 if xsdElement:
576 default = xsdElement.default
577 if default == value or default == _getXmlValue(value):
578 continue
579 elif value == None:
580 continue
581
582 # ToDO remove maxOccurs hack after bug 177 is fixed
583 if attr == "maxOccurs" and value == -1:
584 value = "unbounded"
585
586 if isinstance(value, bool):
587 if value == True:
588 value = "true"
589 else:
590 value = "false"
591
592 attrNameSpacePrefix = ''
593 if hasattr(obj, '__xmlattrnamespaces__'):
bbf7159c 594## print "marshal: found __xmlattrnamespaces__"
1f780e48
RD
595 for nameSpaceKey, nameSpaceAttributes in getattr(obj, '__xmlattrnamespaces__').items():
596 if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as it selement
597 continue
598 if attr in nameSpaceAttributes:
599 attrNameSpacePrefix = nameSpaceKey + ':'
600 break
601## if attr.startswith('_'):
602## attr = attr[1:]
603 if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
bbf7159c 604## print "marshal: found __xmlrename__ (and its attribute)"
1f780e48
RD
605 attr = obj.__xmlrename__[attr]
606
607 objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
bbf7159c 608## print "marshal: new objattrs is: ", objattrs
1f780e48
RD
609
610 objtype = type(obj)
611 if isinstance(obj, NoneType):
bbf7159c 612 #print "marshal: skipping an element with no type"
1f780e48
RD
613 return ''
614# return '%s<%s objtype="None"/>%s' % (prefix, elementName, newline)
615 elif isinstance(obj, bool):
bbf7159c
RD
616 xmlString = '%s<%s objtype="bool">%s</%s>%s' % (prefix, elementName, obj, elementName, newline)
617 #print "marshal: returning a bool element: \n", xmlString
618 return xmlString
1f780e48 619 elif isinstance(obj, int):
bbf7159c
RD
620 xmlString = '''%s<%s objtype="int">%s</%s>%s''' % (prefix, elementName, str(obj), elementName, newline)
621 #print "marshal: returning a int element: \n", xmlString
622 return xmlString
1f780e48 623 elif isinstance(obj, long):
bbf7159c
RD
624 xmlString = '%s<%s objtype="long">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
625 #print "marshal: returning a long element: \n", xmlString
626 return xmlString
1f780e48 627 elif isinstance(obj, float):
bbf7159c
RD
628 xmlString = '%s<%s objtype="float">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
629 #print "marshal: returning a float element: \n", xmlString
630 return xmlString
1f780e48 631 elif isinstance(obj, basestring):
bbf7159c
RD
632 xmlString = '''%s<%s>%s</%s>%s''' % (prefix, elementName, saxutils.escape(obj), elementName, newline)
633 #print "marshal: returning a str element: \n", xmlString
634 return xmlString
1f780e48
RD
635## elif isinstance(obj, unicode):
636## return '''%s<%s>%s</%s>%s''' % (prefix, elementName, obj, elementName, newline)
637 elif isinstance(obj, list):
638 if len(obj) < 1:
bbf7159c 639 #print "marshal: skipping an empty list"
1f780e48
RD
640 return ''
641 xmlString = '%s<%s objtype="list">%s' % (prefix, elementName, newline)
642 for item in obj:
bbf7159c 643 xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)
1f780e48 644 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
bbf7159c 645 #print "marshal: returning a list element: \n", xmlString
1f780e48
RD
646 return xmlString
647 elif isinstance(obj, tuple):
648 if len(obj) < 1:
bbf7159c 649 #print "marshal: skipping an empty tuple"
1f780e48
RD
650 return ''
651 xmlString = '%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)
652 for item in obj:
bbf7159c 653 xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)
1f780e48 654 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
bbf7159c 655 #print "marshal: returning a tuple element: \n", xmlString
1f780e48
RD
656 return xmlString
657 elif isinstance(obj, dict):
658 xmlString = '%s<%s objtype="dict">%s' % (prefix, elementName, newline)
659 subprefix = prefix + ' '*increment
660 subindent = indent + 2*increment
661 for key, val in obj.iteritems():
662 xmlString += "%s<key>%s%s%s</key>%s%s<value>%s%s%s</value>%s" \
bbf7159c 663 % (subprefix, newline, marshal(key, indent=subindent, knownTypes=knownTypes), subprefix, newline, subprefix, newline, marshal(val, nameSpaces=nameSpaces, indent=subindent, knownTypes=knownTypes), subprefix, newline)
1f780e48 664 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
bbf7159c 665 #print "marshal: returning a dict element: \n", xmlString
1f780e48
RD
666 return xmlString
667 else:
668 moduleName = obj.__class__.__module__
669 if (moduleName == "activegrid.model.schema"):
bbf7159c 670## print "marshal: found an activegrid.model.schema class element"
1f780e48
RD
671 xmlString = '%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)
672 else:
bbf7159c
RD
673## print "marshal: found a ", moduleName, " class element"
674 # Only add the objtype if the element tag is unknown to us.
675 try:
676 objname = knownTypes[elementName]
677## print "successfully mapped ", elementName, " to known-objtype ", objname
678 xmlString = '%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)
679 except KeyError:
680## print "failed to map elementName: ", elementName, "; knownTypes: ", knownTypes
681 xmlString = '%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, moduleName, className)
682## print "UnknownTypeException: Unknown type (%s.%s) passed to marshaller" % (moduleName, className)
1f780e48
RD
683 # get the member, value pairs for the object, filtering out
684 # the types we don't support.
bbf7159c
RD
685## print "marshal: elementString: \n", xmlString
686 if (elementAdd != None):
687 prefix += increment*' '
688 indent += increment
689
1f780e48
RD
690 xmlMemberString = ''
691 if hasattr(obj, '__xmlbody__'):
692 xmlMemberString = getattr(obj, obj.__xmlbody__)
693 else:
694 entryList = obj.__dict__.items()
695## # Add in properties
696## for key in obj.__class__.__dict__.iterkeys():
697## if (key not in members_to_skip and key not in obj.__dict__
698## and hasPropertyValue(obj, key)):
699## value = getattr(obj, key)
700## entryList.append((key, value))
701 entryList.sort()
bbf7159c
RD
702 if hasattr(obj, '__xmlattrgroups__'):
703 attrGroups = obj.__xmlattrgroups__
704 if (not isinstance(attrGroups,dict)):
705 raise "__xmlattrgroups__ is not a dict, but must be"
706 for n in attrGroups:
707 v = attrGroups[n]
708 members_to_skip += v
709 else:
710 attrGroups = {}
711 # add the list of all attributes to attrGroups
712 eList = []
713 for x, z in entryList:
714 eList.append(x)
715 attrGroups['__nogroup__'] = eList
716
717 for eName in attrGroups:
718 eList = attrGroups[eName]
719 if (eName != '__nogroup__'):
720 prefix += increment*' '
721 indent += increment
722 xmlMemberString += '%s<%s objtype="None">%s' % (prefix, eName, newline)
723 for name in eList:
724 value = obj.__dict__[name]
725## print " ", name, " = ", value
726## # special name handling for private "__*" attributes:
727## # remove the _<class-name> added by Python
728## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
729 if eName == '__nogroup__' and name in members_to_skip: continue
730 if name.startswith('__') and name.endswith('__'): continue
731## idx = name.find('__')
732## if idx > 0:
733## newName = name[idx+2:]
734## if newName:
735## name = newName
736## print "marshal: processing subElement ", name
737 subElementNameSpacePrefix = nameSpacePrefix
738 if hasattr(obj, '__xmlattrnamespaces__'):
739 for nameSpaceKey, nameSpaceValues in getattr(obj, '__xmlattrnamespaces__').items():
740 if name in nameSpaceValues:
741 subElementNameSpacePrefix = nameSpaceKey + ':'
742 break
743 # handle sequences listed in __xmlflattensequence__
744 # specially: instead of listing the contained items inside
745 # of a separate list, as god intended, list them inside
746 # the object containing the sequence.
747 if hasattr(obj, '__xmlflattensequence__') and name in obj.__xmlflattensequence__ and value:
748 try:
749 xmlnametuple = obj.__xmlflattensequence__[name]
750 xmlname = None
751 if len(xmlnametuple) == 1:
752 xmlname = xmlnametuple[0]
753 except:
754 xmlname = name
755## xmlname = name.lower()
756 for seqitem in value:
757 xmlMemberString += marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)
1f780e48 758 else:
bbf7159c
RD
759 if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
760 xmlname = obj.__xmlrename__[name]
761 else:
762 xmlname = name
763## xmlname = name.lower()
764## # skip
765## if xmlname.startswith('_') and not xmlname.startswith('__'):
766## xmlname = xmlname[1:]
767## if (indent > 30):
768## print "getting pretty deep, xmlname = ", xmlname
769## print "marshal: marshalling ", xmlname
770 xmlMemberString += marshal(value, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment, knownTypes=knownTypes)
771## print "marshal: back with new xmlMemberString: \n", xmlMemberString
772 if (eName != '__nogroup__'):
773## print "marshal: Completing attrGroup ", eName
774 xmlMemberString += '%s</%s>%s' % (prefix, eName, newline)
775 prefix = prefix[:-increment]
776 indent -= increment
777
1f780e48
RD
778 # if we have nested elements, add them here, otherwise close the element tag immediately.
779 if xmlMemberString:
780 xmlString += '>'
781 if hasattr(obj, '__xmlbody__'):
782 xmlString += xmlMemberString
783 xmlString += '</%s>%s' % (elementName, newline)
784 else:
785 xmlString += newline
bbf7159c
RD
786 if (elementAdd != None):
787 xmlString += '%s<%s>%s' % (prefix, elementAdd, newline)
1f780e48 788 xmlString += xmlMemberString
bbf7159c
RD
789 if (elementAdd != None):
790 xmlString += '%s</%s>%s' % (prefix, elementAdd, newline)
791 prefix = prefix[:-increment]
792 indent -= increment
1f780e48
RD
793 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
794 else:
795 xmlString = xmlString + '/>%s' % newline
796 return xmlString
797
798# We don't use this anymore but in case we want to get properties this is how
799# you do it
800def hasPropertyValue(obj, attr):
801 hasProp = False
802 try:
803 prop = obj.__class__.__dict__[attr]
804 if (isinstance(prop, property)):
805 hasProp = hasattr(obj, attr)
806 if (hasProp):
807 # It's a property and it has a value but sometimes we don't want it.
808 # If there is a _hasattr method execute it and the
809 # result will tell us whether to include this value
810 try:
811 hasProp = obj._hasattr(attr)
812 except:
813 pass
814 except KeyError:
815 pass
816 return hasProp
817
818if __name__ == '__main__':
819 from xmlmarshallertests import Person, marshalledint, marshalledlist
820
821 l = [1, 2, 3]
822 d = {'1': 1, '2': 2}
823 outerlist = [l]
824 xmlstr = marshal(d, "d", prettyPrint=True)
825 print xmlstr
826
827 person = Person()
828 person.firstName = "Albert"
829 person.lastName = "Camus"
830 person.addressLine1 = "23 Absurd St."
831 person.city = "Ennui"
832 person.state = "MO"
833 person.zip = "54321"
834 person._phoneNumber = "808-303-2323"
835 person.favoriteWords = ['angst', 'ennui', 'existence']
836 person.weight = 150
837
838 xmlstring = marshal(person, 'person', prettyPrint=True)
839 print xmlstring
840
841 obj = unmarshal(marshalledlist)
842 print "obj has type %s and value %s" % (type(obj), str(obj))
843 for item in obj:
844 print "item: %s" % str(item)