Improvements in XmlMarshaller: any Python variable may be the root of a structure to marshall.

This commit is contained in:
Gaetan Delannay 2010-02-23 14:55:51 +01:00
parent 37c252c3fd
commit 15ef6f334d

View file

@ -17,7 +17,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import xml.sax, difflib import xml.sax, difflib, types
from xml.sax.handler import ContentHandler, ErrorHandler from xml.sax.handler import ContentHandler, ErrorHandler
from xml.sax.xmlreader import InputSource from xml.sax.xmlreader import InputSource
from appy.shared import UnicodeBuffer from appy.shared import UnicodeBuffer
@ -401,6 +401,15 @@ class XmlMarshaller:
self.dumpString(res, value) self.dumpString(res, value)
elif isinstance(value, bool): elif isinstance(value, bool):
res.write(self.trueFalse[value]) res.write(self.trueFalse[value])
elif self.isAnObject(value):
if hasattr(value, 'absolute_url'):
res.write(value.absolute_url())
else:
res.write(value)
# We could dump the entire object content, too. Maybe we could add a
# parameter to the marshaller to know how to marshall objects
# (produce an ID, an URL, include the entire tag but we need to take
# care of circular references,...)
else: else:
res.write(value) res.write(value)
@ -411,12 +420,12 @@ class XmlMarshaller:
fType = None # No type will mean "unicode". fType = None # No type will mean "unicode".
if fieldType == 'file': fType ='file' if fieldType == 'file': fType ='file'
elif fieldType == 'ref': fType = 'list' elif fieldType == 'ref': fType = 'list'
elif isinstance(fieldValue, bool): fType = 'bool' elif isinstance(fieldValue, bool): fType = 'bool'
elif isinstance(fieldValue, int): fType = 'int' elif isinstance(fieldValue, int): fType = 'int'
elif isinstance(fieldValue, float): fType = 'float' elif isinstance(fieldValue, float): fType = 'float'
elif isinstance(fieldValue, long): fType = 'long' elif isinstance(fieldValue, long): fType = 'long'
elif isinstance(fieldValue, tuple): fType = 'tuple' elif isinstance(fieldValue, tuple): fType = 'tuple'
elif isinstance(fieldValue, list): fType = 'list' elif isinstance(fieldValue, list): fType = 'list'
elif fieldValue.__class__.__name__ == 'DateTime': fType = 'DateTime' elif fieldValue.__class__.__name__ == 'DateTime': fType = 'DateTime'
if fType: res.write(' type="%s"' % fType) if fType: res.write(' type="%s"' % fType)
# Dump other attributes if needed # Dump other attributes if needed
@ -434,75 +443,95 @@ class XmlMarshaller:
self.dumpValue(res, fieldValue, fieldType) self.dumpValue(res, fieldValue, fieldType)
res.write('</'); res.write(fieldName); res.write('>') res.write('</'); res.write(fieldName); res.write('>')
def isAnObject(self, instance):
'''Returns True if p_instance is a class instance, False if it is a
basic type, or tuple, sequence, etc.'''
iType = type(instance)
if iType == types.InstanceType:
return True
elif iType.__name__ == 'ImplicitAcquirerWrapper':
# This is the case with Archetype instances
return True
return False
def marshall(self, instance, objectType='popo'): def marshall(self, instance, objectType='popo'):
'''Returns in a UnicodeBuffer the XML version of p_instance. If '''Returns in a UnicodeBuffer the XML version of p_instance. If
p_instance corresponds to a Plain Old Python Object, specify 'popo' p_instance corresponds to a Plain Old Python Object, specify 'popo'
for p_objectType. If p_instance corresponds to an Archetypes object for p_objectType. If p_instance corresponds to an Archetypes object
(Zope/Plone), specify 'archetype' for p_objectType. if p_instance is (Zope/Plone), specify 'archetype' for p_objectType. if p_instance is
a Appy object, specify "appy" as p_objectType.''' a Appy object, specify "appy" as p_objectType. If p_instance is not
an instance at all, but another Python data structure or basic type,
p_objectType is ignored.'''
# Call the XmlMarshaller constructor if it hasn't been called yet.
if not hasattr(self, 'cdata'): if not hasattr(self, 'cdata'):
# The constructor has not been called. Do it now.
XmlMarshaller.__init__(self) XmlMarshaller.__init__(self)
# Create the buffer where the XML result will be dumped.
res = UnicodeBuffer() res = UnicodeBuffer()
# Dump the XML prologue and root element # Dump the XML prologue
if objectType in ('archetype', 'appy'):
objectId = instance.UID() # ID in DB
else: objectId = str(id(instance)) # ID in RAM
res.write(self.xmlPrologue) res.write(self.xmlPrologue)
res.write('<'); res.write(self.rootElementName) if self.isAnObject(instance):
res.write(' type="object" id="'); res.write(objectId); res.write('">') # Determine object ID
# Dump the object ID and the value of the fields that must be dumped if objectType in ('archetype', 'appy'):
if objectType == 'popo': objectId = instance.UID() # ID in DB
for fieldName, fieldValue in instance.__dict__.iteritems(): else:
mustDump = False objectId = str(id(instance)) # ID in RAM
if fieldName in self.fieldsToExclude: res.write('<'); res.write(self.rootElementName)
res.write(' type="object" id="');res.write(objectId);res.write('">')
# Dump the object ID and the value of the fields that must be dumped
if objectType == 'popo':
for fieldName, fieldValue in instance.__dict__.iteritems():
mustDump = False mustDump = False
elif self.fieldsToMarshall == 'all': if fieldName in self.fieldsToExclude:
mustDump = True mustDump = False
else: elif self.fieldsToMarshall == 'all':
if (type(self.fieldsToMarshall) in self.sequenceTypes) and \
(fieldName in self.fieldsToMarshall):
mustDump = True mustDump = True
if mustDump: else:
self.dumpField(res, fieldName, fieldValue) if (type(self.fieldsToMarshall) in self.sequenceTypes) \
elif objectType in ('archetype', 'appy'): and (fieldName in self.fieldsToMarshall):
fields = instance.schema.fields() mustDump = True
for field in instance.schema.fields(): if mustDump:
# Dump only needed fields self.dumpField(res, fieldName, fieldValue)
mustDump = False elif objectType in ('archetype', 'appy'):
if field.getName() in self.fieldsToExclude: fields = instance.schema.fields()
for field in instance.schema.fields():
# Dump only needed fields
mustDump = False mustDump = False
elif (self.fieldsToMarshall == 'all') and \ if field.getName() in self.fieldsToExclude:
(field.schemata != 'metadata'): mustDump = False
mustDump = True elif (self.fieldsToMarshall == 'all') and \
elif self.fieldsToMarshall == 'all_with_metadata': (field.schemata != 'metadata'):
mustDump = True
else:
if (type(self.fieldsToMarshall) in self.sequenceTypes) and \
(field.getName() in self.fieldsToMarshall):
mustDump = True mustDump = True
if mustDump: elif self.fieldsToMarshall == 'all_with_metadata':
fieldType = 'basic' mustDump = True
if field.type in self.atFiles: else:
fieldType = 'file' if (type(self.fieldsToMarshall) in self.sequenceTypes) \
elif field.type == 'reference': and (field.getName() in self.fieldsToMarshall):
fieldType = 'ref' mustDump = True
self.dumpField(res, field.getName(), field.get(instance), if mustDump:
fieldType=fieldType) fieldType = 'basic'
if objectType == 'appy': if field.type in self.atFiles:
# Dump the object history. fieldType = 'file'
res.write('<history type="list">') elif field.type == 'reference':
wfInfo = instance.portal_workflow.getWorkflowsFor(instance) fieldType = 'ref'
if wfInfo: self.dumpField(res, field.getName(),field.get(instance),
history = instance.workflow_history[wfInfo[0].id] fieldType=fieldType)
for event in history: if objectType == 'appy':
res.write('<event type="object">') # Dump the object history.
for k, v in event.iteritems(): self.dumpField(res, k, v) res.write('<history type="list">')
res.write('</event>') wfInfo = instance.portal_workflow.getWorkflowsFor(instance)
res.write('</history>') if wfInfo:
self.marshallSpecificElements(instance, res) history = instance.workflow_history[wfInfo[0].id]
for event in history:
res.write('<event type="object">')
for k, v in event.iteritems():
self.dumpField(res, k, v)
res.write('</event>')
res.write('</history>')
self.marshallSpecificElements(instance, res)
res.write('</'); res.write(self.rootElementName); res.write('>')
else:
self.dumpField(res, self.rootElementName, instance)
# Return the result # Return the result
res.write('</'); res.write(self.rootElementName); res.write('>')
res = res.getValue() res = res.getValue()
if not self.dumpUnicode: if not self.dumpUnicode:
res = res.encode('utf-8') res = res.encode('utf-8')