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