1 #----------------------------------------------------------------------------
2 # Name: xmlmarshaller.py
5 # Author: John Spurling
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12 from activegrid
import util
16 import xml
.sax
.handler
18 from xml
.sax
import saxutils
20 ### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
24 More documentation later, but here are some special Python attributes
25 that McLane recognizes:
29 description: the name of the xml element for the marshalled object
31 name: __xmlattributes__
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.
40 description: the name(s) of the python attribute(s) to skip when
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
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.:
58 <!-- normal way of marshalling -->
60 <item objtype='int'>1</item>
61 <item objtype='int'>2</item>
63 <!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
64 <squish objtype='int'>1</squish>
65 <squish objtype='int'>2</squish>
67 name: __xmlnamespaces__
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.:
73 __xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
75 name: __xmldefaultnamespace__
77 description: the prefix of a namespace defined in __xmlnamespaces__ that
78 should be used as the default namespace for the object.
80 name: __xmlattrnamespaces__
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.:
87 __xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
89 name: __xmlattrgroups__
91 description: a dict specifying groups of attributes to be wrapped in an enclosing tag.
92 The key is the name of the enclosing tag; the value is a list of attributes to include
95 __xmlattrgroups__ = {"name": ["firstName", "lastName"], "address": ["addressLine1", "city", "state", "zip"]}
99 ################################################################################
103 ################################################################################
105 class Error(Exception):
106 """Base class for errors in this module."""
109 class UnhandledTypeException(Error
):
110 """Exception raised when attempting to marshal an unsupported
113 def __init__(self
, typename
):
114 self
.typename
= typename
116 return "%s is not supported for marshalling." % str(self
.typename
)
118 class 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
123 def __init__(self
, attrname
, typename
):
124 self
.attrname
= attrname
125 self
.typename
= typename
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
)
131 ################################################################################
135 ################################################################################
138 XMLNS_PREFIX
= XMLNS
+ ':'
139 XMLNS_PREFIX_LENGTH
= len(XMLNS_PREFIX
)
141 BASETYPE_ELEMENT_NAME
= 'item'
143 # This list doesn't seem to be used.
144 # Internal documentation or useless? You make the call!
145 MEMBERS_TO_SKIP
= ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
146 '__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
147 '__xmldefaultnamespace__', '__xmlattrnamespaces__',
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)
196 ################################################################################
198 # classes and functions
200 ################################################################################
202 def _objectfactory(objname
, objargs
=None, xsname
=None):
204 '''dynamically create an object based on the objname and return
205 it. look it up in the BASETYPE_ELEMENT_MAP first.
207 # split the objname into the typename and module path,
208 # importing the module if need be.
209 if not isinstance(objargs
, list):
214 objname
= knownGlobalTypes
[xsname
]
218 ## print "[objectfactory] creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
219 objtype
= objname
.split('.')[-1]
220 pathlist
= objname
.split('.')
221 modulename
= '.'.join(pathlist
[0:-1])
223 ## print "[objectfactory] objtype is %s" % objtype
224 ## print "[objectfactory] objargs is %s" % `objargs`
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':
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
]
240 objargs
= [saxutils
.unescape(x
) for x
in objargs
]
242 if __builtin__
.__dict
__.has_key(objname
):
246 module
= __import__(modulename
)
247 for name
in pathlist
[1:-1]:
248 module
= module
.__dict
__[name
]
250 return module
.__dict
__[objtype
](*objargs
)
252 if objtype
== 'None':
254 return module
.__dict
__[objtype
]()
256 raise KeyError("Could not find class %s" % objname
)
259 def __init__(self
, name
, attrs
=None):
264 def getobjtype(self
):
265 if self
.attrs
.has_key('objtype'):
266 return self
.attrs
.getValue('objtype')
270 print " name = ", self
.name
, "; attrs = ", self
.attrs
, "number of children = ", len(self
.children
)
272 for child
in self
.children
:
274 childClass
= child
.__class
__.__name
__
275 print " Child ", i
, " class: ",childClass
278 class XMLObjectFactory(xml
.sax
.ContentHandler
):
280 self
.rootelement
= None
281 self
.elementstack
= []
282 xml
.sax
.handler
.ContentHandler
.__init
__(self
)
285 print "-----XMLObjectFactory Dump-------------------------------"
286 if (self
.rootelement
== None):
287 print "rootelement is None"
289 print "rootelement is an object"
291 print "length of elementstack is: ", len(self
.elementstack
)
292 for e
in self
.elementstack
:
294 print "elementstack[", i
, "]: "
296 print "-----end XMLObjectFactory--------------------------------"
298 ## ContentHandler methods
299 def startElement(self
, name
, attrs
):
300 ## print "startElement for name: ", name
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
309 def characters(self
, content
):
310 ## print "got content: %s" % content
312 self
.elementstack
[-1].content
+= content
314 def endElement(self
, name
):
315 ## print "[endElement] name of element we're at the end of: %s" % name
317 if name
.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
318 name
= name
[name
.index(':') + 1:]
319 oldChildren
= self
.elementstack
[-1].children
320 element
= self
.elementstack
.pop()
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]
326 objtype
= element
.getobjtype()
327 ## print "element objtype is: ", objtype
328 if (objtype
== "None"):
329 ## print "[endElement] %s: skipping a (objtype==None) end tag" % name
331 constructorarglist
= []
333 strippedElementContent
= element
.content
.strip()
334 if strippedElementContent
:
335 constructorarglist
.append(element
.content
)
336 ## print "[endElement] calling objectfactory"
337 obj
= _objectfactory(objtype
, constructorarglist
, xsname
)
339 if hasattr(obj
, '__xsdcomplextype__'):
340 complexType
= getattr(obj
, '__xsdcomplextype__')
341 if (hasattr(obj
, '__xmlname__') and getattr(obj
, '__xmlname__') == "sequence"):
342 ## print "[endElement] sequence found"
344 self
.elementstack
[-1].children
= oldChildren
346 ## print "done moving sequence stuff; returning"
348 if len(self
.elementstack
) > 0:
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"
353 self
.rootelement
= obj
354 if element
.attrs
and not isinstance(obj
, list):
355 ## print "[endElement] %s: element has attrs and the obj is not a list" % name
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
:]
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:]
373 xsdElement
= complexType
.findElement(attrname
)
375 type = xsdElement
.type
377 type = xsdToPythonType(type)
378 ### ToDO remove maxOccurs hack after bug 177 is fixed
379 if attrname
== "maxOccurs" and attr
== "unbounded":
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__
386 if hasattr(obj
, '__xmlflattensequence__'):
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
399 raise "Invalid type for __xmlflattensequence___ : it must be a dict, list, or tuple"
401 # reattach an object's attributes to it
402 for childname
, child
in element
.children
:
403 ## print "[endElement] childname is: ", childname, "; child is: ", child
404 if flattenDict
.has_key(childname
):
405 sequencename
= _toAttrName(obj
, flattenDict
[childname
])
406 ## print "[endElement] sequencename is: ", sequencename
408 ## print "[endElement] obj.__dict__ is: ", obj.__dict__
409 sequencevalue
= obj
.__dict
__[sequencename
]
410 except AttributeError:
414 if sequencevalue
== None:
416 obj
.__dict
__[sequencename
] = sequencevalue
417 sequencevalue
.append(child
)
418 elif isinstance(obj
, list):
419 ## print "appended childname = ", childname
422 ## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
423 util
.setattrignorecase(obj
, _toAttrName(obj
, childname
), child
)
424 obj
.__dict
__[_toAttrName(obj
, childname
)] = child
427 for element
in complexType
.elements
:
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
435 def getRootObject(self
):
436 return self
.rootelement
438 def _toAttrName(obj
, name
):
439 if (hasattr(obj
, "__xmlrename__")):
440 for key
, val
in obj
.__xmlrename
__.iteritems():
444 ## if (name.startswith("__") and not name.endswith("__")):
445 ## name = "_%s%s" % (obj.__class__.__name__, name)
448 __typeMappingXsdToPython
= {
452 "date": "str", # ToDO Need to work out how to create python date types
454 "decimal": "float", # ToDO Does python have a better fixed point type?
460 "unicode": "unicode",
462 "duration": "str", # see above (date)
463 "datetime": "str", # see above (date)
464 "time": "str", # see above (date)
468 def xsdToPythonType(xsdType
):
470 return __typeMappingXsdToPython
[xsdType
]
472 raise Exception("Unknown xsd type %s" % xsdType
)
474 def _getXmlValue(pythonValue
):
475 if (isinstance(pythonValue
, bool)):
476 return str(pythonValue
).lower()
478 return str(pythonValue
)
480 def unmarshal(xmlstr
, knownTypes
=None):
481 global knownGlobalTypes
482 if (knownTypes
== None):
483 knownGlobalTypes
= {}
485 knownGlobalTypes
= knownTypes
486 objectfactory
= XMLObjectFactory()
487 xml
.sax
.parseString(xmlstr
, objectfactory
)
488 return objectfactory
.getRootObject()
491 def marshal(obj
, elementName
=None, nameSpacePrefix
='', nameSpaces
=None, prettyPrint
=False, indent
=0, knownTypes
=None):
492 if prettyPrint
or indent
:
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
506 if knownTypes
== None:
508 if hasattr(obj
, '__xmlnamespaces__'):
509 for nameSpaceKey
, nameSpaceUrl
in getattr(obj
, '__xmlnamespaces__').items():
510 if nameSpaceUrl
in nameSpaces
:
511 nameSpaceKey
= nameSpaces
[nameSpaceUrl
]
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
516 ## while nameSpaceKey in nameSpaces.values():
517 ## nameSpaceKey = origNameSpaceKey + str(i)
519 nameSpaces
[nameSpaceUrl
] = nameSpaceKey
520 if nameSpaceKey
== '':
521 nameSpaceAttrs
+= ' xmlns="%s" ' % (nameSpaceUrl
)
523 nameSpaceAttrs
+= ' xmlns:%s="%s" ' % (nameSpaceKey
, nameSpaceUrl
)
524 nameSpaceAttrs
= nameSpaceAttrs
.rstrip()
525 if hasattr(obj
, '__xmldefaultnamespace__'):
526 nameSpacePrefix
= getattr(obj
, '__xmldefaultnamespace__') + ':'
528 if hasattr(obj
, '__xmlname__'):
529 elementName
= nameSpacePrefix
+ obj
.__xmlname
__
531 elementName
= nameSpacePrefix
+ BASETYPE_ELEMENT_NAME
533 elementName
= nameSpacePrefix
+ elementName
534 if hasattr(obj
, '__xmlsequencer__'):
535 elementAdd
= obj
.__xmlsequencer
__
539 ## print "marshal: entered with elementName: ", elementName
541 ## Add more members_to_skip based on ones the user has selected
542 ## via the __xmlexclude__ attribute.
543 if hasattr(obj
, '__xmlexclude__'):
544 ## print "marshal: found __xmlexclude__"
545 members_to_skip
+= list(obj
.__xmlexclude
__)
546 # Marshal the attributes that are selected to be XML attributes.
548 className
= obj
.__class
__.__name
__
549 classNamePrefix
= "_" + className
550 if hasattr(obj
, '__xmlattributes__'):
551 ## print "marshal: found __xmlattributes__"
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.
560 ## print "marshal: processing attribute ", internalAttrName
562 value
= obj
.__dict
__[internalAttrName
]
565 ## # But, check and see if it is a property first:
566 ## if (hasPropertyValue(obj, attr)):
567 ## value = getattr(obj, attr)
571 if hasattr(obj
, '__xsdcomplextype__'):
572 ## print "marshal: found __xsdcomplextype__"
573 complexType
= getattr(obj
, '__xsdcomplextype__')
574 xsdElement
= complexType
.findElement(attr
)
576 default
= xsdElement
.default
577 if default
== value
or default
== _getXmlValue(value
):
582 # ToDO remove maxOccurs hack after bug 177 is fixed
583 if attr
== "maxOccurs" and value
== -1:
586 if isinstance(value
, bool):
592 attrNameSpacePrefix
= ''
593 if hasattr(obj
, '__xmlattrnamespaces__'):
594 ## print "marshal: found __xmlattrnamespaces__"
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
598 if attr
in nameSpaceAttributes
:
599 attrNameSpacePrefix
= nameSpaceKey
+ ':'
601 ## if attr.startswith('_'):
603 if (hasattr(obj
, "__xmlrename__") and attr
in obj
.__xmlrename
__):
604 ## print "marshal: found __xmlrename__ (and its attribute)"
605 attr
= obj
.__xmlrename
__[attr
]
607 objattrs
+= ' %s%s="%s"' % (attrNameSpacePrefix
, attr
, value
)
608 ## print "marshal: new objattrs is: ", objattrs
611 if isinstance(obj
, NoneType
):
612 #print "marshal: skipping an element with no type"
614 # return '%s<%s objtype="None"/>%s' % (prefix, elementName, newline)
615 elif isinstance(obj
, bool):
616 xmlString
= '%s<%s objtype="bool">%s</%s>%s' % (prefix
, elementName
, obj
, elementName
, newline
)
617 #print "marshal: returning a bool element: \n", xmlString
619 elif isinstance(obj
, int):
620 xmlString
= '''%s<%s objtype="int">%s</%s>%s''' % (prefix
, elementName
, str(obj
), elementName
, newline
)
621 #print "marshal: returning a int element: \n", xmlString
623 elif isinstance(obj
, long):
624 xmlString
= '%s<%s objtype="long">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)
625 #print "marshal: returning a long element: \n", xmlString
627 elif isinstance(obj
, float):
628 xmlString
= '%s<%s objtype="float">%s</%s>%s' % (prefix
, elementName
, str(obj
), elementName
, newline
)
629 #print "marshal: returning a float element: \n", xmlString
631 elif isinstance(obj
, basestring
):
632 xmlString
= '''%s<%s>%s</%s>%s''' % (prefix
, elementName
, saxutils
.escape(obj
), elementName
, newline
)
633 #print "marshal: returning a str element: \n", xmlString
635 ## elif isinstance(obj, unicode):
636 ## return '''%s<%s>%s</%s>%s''' % (prefix, elementName, obj, elementName, newline)
637 elif isinstance(obj
, list):
639 #print "marshal: skipping an empty list"
641 xmlString
= '%s<%s objtype="list">%s' % (prefix
, elementName
, newline
)
643 xmlString
+= marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
)
644 xmlString
+= '%s</%s>%s' % (prefix
, elementName
, newline
)
645 #print "marshal: returning a list element: \n", xmlString
647 elif isinstance(obj
, tuple):
649 #print "marshal: skipping an empty tuple"
651 xmlString
= '%s<%s objtype="list" mutable="false">%s' % (prefix
, elementName
, newline
)
653 xmlString
+= marshal(item
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
)
654 xmlString
+= '%s</%s>%s' % (prefix
, elementName
, newline
)
655 #print "marshal: returning a tuple element: \n", 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" \
663 % (subprefix
, newline
, marshal(key
, indent
=subindent
, knownTypes
=knownTypes
), subprefix
, newline
, subprefix
, newline
, marshal(val
, nameSpaces
=nameSpaces
, indent
=subindent
, knownTypes
=knownTypes
), subprefix
, newline
)
664 xmlString
+= '%s</%s>%s' % (prefix
, elementName
, newline
)
665 #print "marshal: returning a dict element: \n", xmlString
668 moduleName
= obj
.__class
__.__module
__
669 if (moduleName
== "activegrid.model.schema"):
670 ## print "marshal: found an activegrid.model.schema class element"
671 xmlString
= '%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)
673 ## print "marshal: found a ", moduleName, " class element"
674 # Only add the objtype if the element tag is unknown to us.
676 objname
= knownTypes
[elementName
]
677 ## print "successfully mapped ", elementName, " to known-objtype ", objname
678 xmlString
= '%s<%s%s%s' % (prefix
, elementName
, nameSpaceAttrs
, objattrs
)
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)
683 # get the member, value pairs for the object, filtering out
684 # the types we don't support.
685 ## print "marshal: elementString: \n", xmlString
686 if (elementAdd
!= None):
687 prefix
+= increment
*' '
691 if hasattr(obj
, '__xmlbody__'):
692 xmlMemberString
= getattr(obj
, obj
.__xmlbody
__)
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))
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"
711 # add the list of all attributes to attrGroups
713 for x
, z
in entryList
:
715 attrGroups
['__nogroup__'] = eList
717 for eName
in attrGroups
:
718 eList
= attrGroups
[eName
]
719 if (eName
!= '__nogroup__'):
720 prefix
+= increment
*' '
722 xmlMemberString
+= '%s<%s objtype="None">%s' % (prefix
, eName
, newline
)
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('__')
733 ## newName = name[idx+2:]
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
+ ':'
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
:
749 xmlnametuple
= obj
.__xmlflattensequence
__[name
]
751 if len(xmlnametuple
) == 1:
752 xmlname
= xmlnametuple
[0]
755 ## xmlname = name.lower()
756 for seqitem
in value
:
757 xmlMemberString
+= marshal(seqitem
, xmlname
, subElementNameSpacePrefix
, nameSpaces
=nameSpaces
, indent
=indent
+increment
, knownTypes
=knownTypes
)
759 if (hasattr(obj
, "__xmlrename__") and name
in obj
.__xmlrename
__):
760 xmlname
= obj
.__xmlrename
__[name
]
763 ## xmlname = name.lower()
765 ## if xmlname.startswith('_') and not xmlname.startswith('__'):
766 ## xmlname = xmlname[1:]
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
]
778 # if we have nested elements, add them here, otherwise close the element tag immediately.
781 if hasattr(obj
, '__xmlbody__'):
782 xmlString
+= xmlMemberString
783 xmlString
+= '</%s>%s' % (elementName
, newline
)
786 if (elementAdd
!= None):
787 xmlString
+= '%s<%s>%s' % (prefix
, elementAdd
, newline
)
788 xmlString
+= xmlMemberString
789 if (elementAdd
!= None):
790 xmlString
+= '%s</%s>%s' % (prefix
, elementAdd
, newline
)
791 prefix
= prefix
[:-increment
]
793 xmlString
+= '%s</%s>%s' % (prefix
, elementName
, newline
)
795 xmlString
= xmlString
+ '/>%s' % newline
798 # We don't use this anymore but in case we want to get properties this is how
800 def hasPropertyValue(obj
, attr
):
803 prop
= obj
.__class
__.__dict
__[attr
]
804 if (isinstance(prop
, property)):
805 hasProp
= hasattr(obj
, attr
)
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
811 hasProp
= obj
._hasattr
(attr
)
818 if __name__
== '__main__':
819 from xmlmarshallertests
import Person
, marshalledint
, marshalledlist
824 xmlstr
= marshal(d
, "d", prettyPrint
=True)
828 person
.firstName
= "Albert"
829 person
.lastName
= "Camus"
830 person
.addressLine1
= "23 Absurd St."
831 person
.city
= "Ennui"
834 person
._phoneNumber
= "808-303-2323"
835 person
.favoriteWords
= ['angst', 'ennui', 'existence']
838 xmlstring
= marshal(person
, 'person', prettyPrint
=True)
841 obj
= unmarshal(marshalledlist
)
842 print "obj has type %s and value %s" % (type(obj
), str(obj
))
844 print "item: %s" % str(item
)