]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/util/xmlmarshaller.py
Added the ActiveGrid IDE as a sample application
[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 from activegrid import util
13 import inspect
14 from types import *
15 import xml.sax
16 import xml.sax.handler
17 import __builtin__
18 from xml.sax import saxutils
19
20 ### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
21
22 """
23
24 More documentation later, but here are some special Python attributes
25 that McLane recognizes:
26
27 name: __xmlname__
28 type: string
29 description: the name of the xml element for the marshalled object
30
31 name: __xmlattributes__
32 type: tuple or list
33 description: the name(s) of the Python string attribute(s) to be
34 marshalled as xml attributes instead of nested xml elements. currently
35 these can only be strings since there's not a way to get the type
36 information back when unmarshalling.
37
38 name: __xmlexclude__
39 type: tuple or list
40 description: the name(s) of the python attribute(s) to skip when
41 marshalling.
42
43 name: __xmlrename__
44 type: dict
45 description: describes an alternate Python <-> XML name mapping.
46 Normally the name mapping is the identity function. __xmlrename__
47 overrides that. The keys are the Python names, the values are their
48 associated XML names.
49
50 name: __xmlflattensequence__
51 type: dict, tuple, or list
52 description: the name(s) of the Python sequence attribute(s) whose
53 items are to be marshalled as a series of xml elements (with an
54 optional keyword argument that specifies the element name to use) as
55 opposed to containing them in a separate sequence element, e.g.:
56
57 myseq = (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
67 name: __xmlnamespaces__
68 type: dict
69 description: a dict of the namespaces that the object uses. Each item
70 in the dict should consist of a prefix,url combination where the key is
71 the prefix and url is the value, e.g.:
72
73 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
74
75 name: __xmldefaultnamespace__
76 type: String
77 description: the prefix of a namespace defined in __xmlnamespaces__ that
78 should be used as the default namespace for the object.
79
80 name: __xmlattrnamespaces__
81 type: dict
82 description: a dict assigning the Python object's attributes to the namespaces
83 defined in __xmlnamespaces__. Each item in the dict should consist of a
84 prefix,attributeList combination where the key is the prefix and the value is
85 a list of the Python attribute names. e.g.:
86
87 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
88
89
90 """
91
92 ################################################################################
93 #
94 # module exceptions
95 #
96 ################################################################################
97
98 class Error(Exception):
99 """Base class for errors in this module."""
100 pass
101
102 class UnhandledTypeException(Error):
103 """Exception raised when attempting to marshal an unsupported
104 type.
105 """
106 def __init__(self, typename):
107 self.typename = typename
108 def __str__(self):
109 return "%s is not supported for marshalling." % str(self.typename)
110
111 class XMLAttributeIsNotStringType(Error):
112 """Exception raised when an object's attribute is specified to be
113 marshalled as an XML attribute of the enclosing object instead of
114 a nested element.
115 """
116 def __init__(self, attrname, typename):
117 self.attrname = attrname
118 self.typename = typename
119 def __str__(self):
120 return """%s was set to be marshalled as an XML attribute
121 instead of a nested element, but the object's type is %s, not
122 string.""" % (self.attrname, self.typename)
123
124 ################################################################################
125 #
126 # constants and such
127 #
128 ################################################################################
129
130 XMLNS = 'xmlns'
131 XMLNS_PREFIX = XMLNS + ':'
132 XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
133
134 BASETYPE_ELEMENT_NAME = 'item'
135 MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
136 '__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
137 '__xmldefaultnamespace__', '__xmlattrnamespaces__')
138
139 WELL_KNOWN_OBJECTS = { "xs:element" : "activegrid.model.schema.XsdElement",
140 "xs:complexType" : "activegrid.model.schema.XsdComplexType",
141 "xs:complexType" : "activegrid.model.schema.XsdComplexType",
142 "xs:element" : "activegrid.model.schema.XsdElement",
143 "xs:key" : "activegrid.model.schema.XsdKey",
144 "xs:field" : "activegrid.model.schema.XsdKeyField",
145 "xs:keyref" : "activegrid.model.schema.XsdKeyRef",
146 "xs:selector" : "activegrid.model.schema.XsdKeySelector",
147 "xs:schema" : "activegrid.model.schema.Schema",
148 "ag:schemaOptions":"activegrid.model.schema.SchemaOptions",
149 "ag:debug" : "activegrid.model.processmodel.DebugOperation",
150 }
151
152
153 ################################################################################
154 #
155 # classes and functions
156 #
157 ################################################################################
158
159 def _objectfactory(objname, objargs=None, xsname=None):
160 try:
161 '''dynamically create an object based on the objname and return
162 it. look it up in the BASETYPE_ELEMENT_MAP first.
163 '''
164 ## print "_objectfactory creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
165 # split the objname into the typename and module path,
166 # importing the module if need be.
167 if not isinstance(objargs, list):
168 objargs = [objargs]
169
170 if (xsname):
171 try:
172 objname = WELL_KNOWN_OBJECTS[xsname]
173 except KeyError:
174 pass
175
176 objtype = objname.split('.')[-1]
177 pathlist = objname.split('.')
178 modulename = '.'.join(pathlist[0:-1])
179
180 ## print "[objectfactory] objtype is %s" % objtype
181 ## print "[objectfactory] objargs is %s" % `objargs`
182
183 ## since the bool constructor will turn a string of non-zero
184 ## length into True, we call it with no argument (yielding a
185 ## False) if the string contains 'false'
186 if objtype == 'bool' and objargs[0].lower() == 'false':
187 objargs = None
188
189 ## if objtype == 'str':
190 ## print type(objargs)
191 ## print "string we're unescaping: '%s'" % objargs[0]
192 ## objargs = saxutils.unescape(objargs[0])
193 if objtype in ('float', 'int', 'str', 'long'):
194 objargs = [x.strip() for x in objargs]
195
196 if objtype == 'str':
197 objargs = [saxutils.unescape(x) for x in objargs]
198
199 if __builtin__.__dict__.has_key(objname):
200 module = __builtin__
201 else:
202 if modulename:
203 module = __import__(modulename)
204 for name in pathlist[1:-1]:
205 module = module.__dict__[name]
206 if objargs:
207 return module.__dict__[objtype](*objargs)
208 else:
209 if objtype == 'None':
210 return None
211 return module.__dict__[objtype]()
212 except KeyError:
213 raise KeyError("Could not find class %s" % objname)
214
215 class Element:
216 def __init__(self, name, attrs=None):
217 self.name = name
218 self.attrs = attrs
219 self.content = ''
220 self.children = []
221 def getobjtype(self):
222 if self.attrs.has_key('objtype'):
223 return self.attrs.getValue('objtype')
224 else:
225 return 'str'
226
227
228 class XMLObjectFactory(xml.sax.ContentHandler):
229 def __init__(self):
230 self.rootelement = None
231 self.elementstack = []
232 xml.sax.handler.ContentHandler.__init__(self)
233
234 ## ContentHandler methods
235 def startElement(self, name, attrs):
236 if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
237 name = name[name.index(':') + 1:]
238 ## for attrname in attrs.getNames():
239 ## print "%s: %s" % (attrname, attrs.getValue(attrname))
240 element = Element(name, attrs.copy())
241 self.elementstack.append(element)
242 ## print self.elementstack
243
244 def characters(self, content):
245 ## print "got content: %s" % content
246 if content:
247 self.elementstack[-1].content += content
248
249 def endElement(self, name):
250 ## print "[endElement] name of element we're at the end of: %s" % name
251 xsname = name
252 if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
253 name = name[name.index(':') + 1:]
254 element = self.elementstack.pop()
255 objtype = element.getobjtype()
256 constructorarglist = []
257 if element.content:
258 strippedElementContent = element.content.strip()
259 if strippedElementContent:
260 constructorarglist.append(element.content)
261 obj = _objectfactory(objtype, constructorarglist, xsname)
262 complexType = None
263 if hasattr(obj, '__xsdcomplextype__'):
264 complexType = getattr(obj, '__xsdcomplextype__')
265 if len(self.elementstack) > 0:
266 self.elementstack[-1].children.append((name, obj))
267 else:
268 self.rootelement = obj
269 if element.attrs and not isinstance(obj, list):
270 for attrname, attr in element.attrs.items():
271 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
272 if attrname.startswith(XMLNS_PREFIX):
273 ns = attrname[XMLNS_PREFIX_LENGTH:]
274 else:
275 ns = ""
276 if not hasattr(obj, '__xmlnamespaces__'):
277 obj.__xmlnamespaces__ = {ns:attr}
278 elif ns not in obj.__xmlnamespaces__:
279 if (hasattr(obj.__class__, '__xmlnamespaces__')
280 and obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__):
281 obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__)
282 obj.__xmlnamespaces__[ns] = attr
283 elif not attrname == 'objtype':
284 if attrname.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
285 attrname = attrname[attrname.index(':') + 1:]
286 if complexType:
287 xsdElement = complexType.findElement(attrname)
288 if xsdElement:
289 type = xsdElement.type
290 if type:
291 type = xsdToPythonType(type)
292 ### ToDO remove maxOccurs hack after bug 177 is fixed
293 if attrname == "maxOccurs" and attr == "unbounded":
294 attr = "-1"
295 attr = _objectfactory(type, attr)
296 util.setattrignorecase(obj, _toAttrName(obj, attrname), attr)
297 ## obj.__dict__[_toAttrName(obj, attrname)] = attr
298 # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
299 flattenDict = {}
300 if hasattr(obj, '__xmlflattensequence__'):
301 for sequencename, xmlnametuple in obj.__xmlflattensequence__.items():
302 for xmlname in xmlnametuple:
303 flattenDict[xmlname] = sequencename
304
305 # reattach an object's attributes to it
306 for childname, child in element.children:
307 if flattenDict.has_key(childname):
308 sequencename = _toAttrName(obj, flattenDict[childname])
309 try:
310 sequencevalue = obj.__dict__[sequencename]
311 except AttributeError:
312 sequencevalue = None
313 if sequencevalue == None:
314 sequencevalue = []
315 obj.__dict__[sequencename] = sequencevalue
316 sequencevalue.append(child)
317 elif isinstance(obj, list):
318 obj.append(child)
319 else:
320 ## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
321 util.setattrignorecase(obj, _toAttrName(obj, childname), child)
322 ## obj.__dict__[_toAttrName(obj, childname)] = child
323
324 if complexType:
325 for element in complexType.elements:
326 if element.default:
327 elementName = _toAttrName(obj, element.name)
328 if ((elementName not in obj.__dict__) or (obj.__dict__[elementName] == None)):
329 pythonType = xsdToPythonType(element.type)
330 defaultValue = _objectfactory(pythonType, element.default)
331 obj.__dict__[elementName] = defaultValue
332
333 def getRootObject(self):
334 return self.rootelement
335
336 def _toAttrName(obj, name):
337 if (hasattr(obj, "__xmlrename__")):
338 for key, val in obj.__xmlrename__.iteritems():
339 if (name == val):
340 name = key
341 break
342 ## if (name.startswith("__") and not name.endswith("__")):
343 ## name = "_%s%s" % (obj.__class__.__name__, name)
344 return name
345
346 __typeMappingXsdToPython = {
347 "string": "str",
348 "char": "str",
349 "varchar": "str",
350 "date": "str", # ToDO Need to work out how to create python date types
351 "boolean": "bool",
352 "decimal": "float", # ToDO Does python have a better fixed point type?
353 "int": "int",
354 "long": "long",
355 "float": "float",
356 "bool": "bool",
357 "str": "str",
358 "unicode": "unicode",
359 }
360
361 def xsdToPythonType(xsdType):
362 try:
363 return __typeMappingXsdToPython[xsdType]
364 except KeyError:
365 raise Exception("Unknown xsd type %s" % xsdType)
366
367 def _getXmlValue(pythonValue):
368 if (isinstance(pythonValue, bool)):
369 return str(pythonValue).lower()
370 else:
371 return str(pythonValue)
372
373 def unmarshal(xmlstr):
374 objectfactory = XMLObjectFactory()
375 xml.sax.parseString(xmlstr, objectfactory)
376 return objectfactory.getRootObject()
377
378
379 def marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0):
380 if prettyPrint or indent:
381 prefix = ' '*indent
382 newline = '\n'
383 increment = 4
384 else:
385 prefix = ''
386 newline = ''
387 increment = 0
388
389 ## Determine the XML element name. If it isn't specified in the
390 ## parameter list, look for it in the __xmlname__ Python
391 ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
392 if not nameSpaces: nameSpaces = {} # Need to do this since if the {} is a default parameter it gets shared by all calls into the function
393 nameSpaceAttrs = ''
394 if hasattr(obj, '__xmlnamespaces__'):
395 for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
396 if nameSpaceUrl in nameSpaces:
397 nameSpaceKey = nameSpaces[nameSpaceUrl]
398 else:
399 ## # TODO: Wait to do this until there is shared state for use when going through the object graph
400 ## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
401 ## i = 1
402 ## while nameSpaceKey in nameSpaces.values():
403 ## nameSpaceKey = origNameSpaceKey + str(i)
404 ## i += 1
405 nameSpaces[nameSpaceUrl] = nameSpaceKey
406 if nameSpaceKey == '':
407 nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
408 else:
409 nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
410 nameSpaceAttrs = nameSpaceAttrs.rstrip()
411 if hasattr(obj, '__xmldefaultnamespace__'):
412 nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
413 if not elementName:
414 if hasattr(obj, '__xmlname__'):
415 elementName = nameSpacePrefix + obj.__xmlname__
416 else:
417 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
418 else:
419 elementName = nameSpacePrefix + elementName
420
421 members_to_skip = []
422 ## Add more members_to_skip based on ones the user has selected
423 ## via the __xmlexclude__ attribute.
424 if hasattr(obj, '__xmlexclude__'):
425 members_to_skip += list(obj.__xmlexclude__)
426 # Marshal the attributes that are selected to be XML attributes.
427 objattrs = ''
428 className = obj.__class__.__name__
429 classNamePrefix = "_" + className
430 if hasattr(obj, '__xmlattributes__'):
431 xmlattributes = obj.__xmlattributes__
432 members_to_skip += xmlattributes
433 for attr in xmlattributes:
434 internalAttrName = attr
435 if (attr.startswith("__") and not attr.endswith("__")):
436 internalAttrName = classNamePrefix + attr
437 # Fail silently if a python attribute is specified to be
438 # an XML attribute but is missing.
439 try:
440 value = obj.__dict__[internalAttrName]
441 except KeyError:
442 continue
443 ## # But, check and see if it is a property first:
444 ## if (hasPropertyValue(obj, attr)):
445 ## value = getattr(obj, attr)
446 ## else:
447 ## continue
448 xsdElement = None
449 if hasattr(obj, '__xsdcomplextype__'):
450 complexType = getattr(obj, '__xsdcomplextype__')
451 xsdElement = complexType.findElement(attr)
452 if xsdElement:
453 default = xsdElement.default
454 if default == value or default == _getXmlValue(value):
455 continue
456 elif value == None:
457 continue
458
459 # ToDO remove maxOccurs hack after bug 177 is fixed
460 if attr == "maxOccurs" and value == -1:
461 value = "unbounded"
462
463 if isinstance(value, bool):
464 if value == True:
465 value = "true"
466 else:
467 value = "false"
468
469 attrNameSpacePrefix = ''
470 if hasattr(obj, '__xmlattrnamespaces__'):
471 for nameSpaceKey, nameSpaceAttributes in getattr(obj, '__xmlattrnamespaces__').items():
472 if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as it selement
473 continue
474 if attr in nameSpaceAttributes:
475 attrNameSpacePrefix = nameSpaceKey + ':'
476 break
477 ## if attr.startswith('_'):
478 ## attr = attr[1:]
479 if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
480 attr = obj.__xmlrename__[attr]
481
482 objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
483
484 objtype = type(obj)
485 if isinstance(obj, NoneType):
486 return ''
487 # return '%s<%s objtype="None"/>%s' % (prefix, elementName, newline)
488 elif isinstance(obj, bool):
489 return '%s<%s objtype="bool">%s</%s>%s' % (prefix, elementName, obj, elementName, newline)
490 elif isinstance(obj, int):
491 return '''%s<%s objtype="int">%s</%s>%s''' % (prefix, elementName, str(obj), elementName, newline)
492 elif isinstance(obj, long):
493 return '%s<%s objtype="long">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
494 elif isinstance(obj, float):
495 return '%s<%s objtype="float">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
496 elif isinstance(obj, basestring):
497 return '''%s<%s>%s</%s>%s''' % (prefix, elementName, saxutils.escape(obj), elementName, newline)
498 ## elif isinstance(obj, unicode):
499 ## return '''%s<%s>%s</%s>%s''' % (prefix, elementName, obj, elementName, newline)
500 elif isinstance(obj, list):
501 if len(obj) < 1:
502 return ''
503 xmlString = '%s<%s objtype="list">%s' % (prefix, elementName, newline)
504 for item in obj:
505 xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
506 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
507 return xmlString
508 elif isinstance(obj, tuple):
509 if len(obj) < 1:
510 return ''
511 xmlString = '%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)
512 for item in obj:
513 xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
514 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
515 return xmlString
516 elif isinstance(obj, dict):
517 xmlString = '%s<%s objtype="dict">%s' % (prefix, elementName, newline)
518 subprefix = prefix + ' '*increment
519 subindent = indent + 2*increment
520 for key, val in obj.iteritems():
521 xmlString += "%s<key>%s%s%s</key>%s%s<value>%s%s%s</value>%s" \
522 % (subprefix, newline, marshal(key, indent=subindent), subprefix, newline, subprefix, newline, marshal(val, nameSpaces=nameSpaces, indent=subindent), subprefix, newline)
523 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
524 return xmlString
525 else:
526 moduleName = obj.__class__.__module__
527 if (moduleName == "activegrid.model.schema"):
528 xmlString = '%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)
529 else:
530 xmlString = '%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, moduleName, className)
531 # get the member, value pairs for the object, filtering out
532 # the types we don't support.
533 xmlMemberString = ''
534 if hasattr(obj, '__xmlbody__'):
535 xmlMemberString = getattr(obj, obj.__xmlbody__)
536 else:
537 entryList = obj.__dict__.items()
538 ## # Add in properties
539 ## for key in obj.__class__.__dict__.iterkeys():
540 ## if (key not in members_to_skip and key not in obj.__dict__
541 ## and hasPropertyValue(obj, key)):
542 ## value = getattr(obj, key)
543 ## entryList.append((key, value))
544 entryList.sort()
545 for name, value in entryList:
546 ## # special name handling for private "__*" attributes:
547 ## # remove the _<class-name> added by Python
548 ## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
549 if name in members_to_skip: continue
550 if name.startswith('__') and name.endswith('__'): continue
551 ## idx = name.find('__')
552 ## if idx > 0:
553 ## newName = name[idx+2:]
554 ## if newName:
555 ## name = newName
556 subElementNameSpacePrefix = nameSpacePrefix
557 if hasattr(obj, '__xmlattrnamespaces__'):
558 for nameSpaceKey, nameSpaceValues in getattr(obj, '__xmlattrnamespaces__').items():
559 if name in nameSpaceValues:
560 subElementNameSpacePrefix = nameSpaceKey + ':'
561 break
562 # handle sequences listed in __xmlflattensequence__
563 # specially: instead of listing the contained items inside
564 # of a separate list, as god intended, list them inside
565 # the object containing the sequence.
566 if hasattr(obj, '__xmlflattensequence__') and name in obj.__xmlflattensequence__ and value:
567 try:
568 xmlnametuple = obj.__xmlflattensequence__[name]
569 xmlname = None
570 if len(xmlnametuple) == 1:
571 xmlname = xmlnametuple[0]
572 except:
573 xmlname = name
574 ## xmlname = name.lower()
575 for seqitem in value:
576 xmlMemberString += marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
577 else:
578 if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
579 xmlname = obj.__xmlrename__[name]
580 else:
581 xmlname = name
582 ## xmlname = name.lower()
583 ## # skip
584 ## if xmlname.startswith('_') and not xmlname.startswith('__'):
585 ## xmlname = xmlname[1:]
586 ## if (indent > 30):
587 ## print "getting pretty deep, xmlname = ", xmlname
588 xmlMemberString += marshal(value, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
589 # if we have nested elements, add them here, otherwise close the element tag immediately.
590 if xmlMemberString:
591 xmlString += '>'
592 if hasattr(obj, '__xmlbody__'):
593 xmlString += xmlMemberString
594 xmlString += '</%s>%s' % (elementName, newline)
595 else:
596 xmlString += newline
597 xmlString += xmlMemberString
598 xmlString += '%s</%s>%s' % (prefix, elementName, newline)
599 else:
600 xmlString = xmlString + '/>%s' % newline
601 return xmlString
602
603 # We don't use this anymore but in case we want to get properties this is how
604 # you do it
605 def hasPropertyValue(obj, attr):
606 hasProp = False
607 try:
608 prop = obj.__class__.__dict__[attr]
609 if (isinstance(prop, property)):
610 hasProp = hasattr(obj, attr)
611 if (hasProp):
612 # It's a property and it has a value but sometimes we don't want it.
613 # If there is a _hasattr method execute it and the
614 # result will tell us whether to include this value
615 try:
616 hasProp = obj._hasattr(attr)
617 except:
618 pass
619 except KeyError:
620 pass
621 return hasProp
622
623 if __name__ == '__main__':
624 from xmlmarshallertests import Person, marshalledint, marshalledlist
625
626 l = [1, 2, 3]
627 d = {'1': 1, '2': 2}
628 outerlist = [l]
629 xmlstr = marshal(d, "d", prettyPrint=True)
630 print xmlstr
631
632 person = Person()
633 person.firstName = "Albert"
634 person.lastName = "Camus"
635 person.addressLine1 = "23 Absurd St."
636 person.city = "Ennui"
637 person.state = "MO"
638 person.zip = "54321"
639 person._phoneNumber = "808-303-2323"
640 person.favoriteWords = ['angst', 'ennui', 'existence']
641 person.weight = 150
642
643 xmlstring = marshal(person, 'person', prettyPrint=True)
644 print xmlstring
645
646 obj = unmarshal(marshalledlist)
647 print "obj has type %s and value %s" % (type(obj), str(obj))
648 for item in obj:
649 print "item: %s" % str(item)