Back to index

moin  1.9.0~rc2
marshal.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - XML Serialization
00004 
00005     @copyright: 2002 Juergen Hermann <jh@web.de>
00006     @license: GNU GPL, see COPYING for details.
00007 """
00008 
00009 class Marshal:
00010     """ Serialize Python data structures to XML.
00011 
00012         XML_DECL is the standard XML declaration.
00013 
00014         The class attributes ROOT_CONTAINER (default "data") and
00015         ITEM_CONTAINER (default "item") can be overwritten by derived
00016         classes; if ROOT_CONTAINER is `None`, the usually generated
00017         container element is omitted, the same is true for ITEM_CONTAINER.
00018 
00019         PRIVATE_PREFIXES is a list of prefixes of tagnames that are
00020         treated as private, i.e. things that should not be serialized.
00021         Default is to omit all properties starting with an underscore.
00022 
00023         TAG_MAP is a translation table for tag names, and is empty by
00024         default. It can also be used to suppress certain properties by
00025         mapping a tag name to `None`.
00026     """
00027 
00028     # Convenience: Standard XML declaration
00029     XML_DECL = '<?xml version="1.0" encoding="utf-8"?>\n'
00030 
00031     # Container Tags
00032     ROOT_CONTAINER = "data"
00033     ITEM_CONTAINER = "item"
00034 
00035     # List of private prefixes
00036     PRIVATE_PREFIXES = ['_']
00037 
00038     # Translation map
00039     TAG_MAP = {}
00040 
00041 
00042     def __toXML(self, element, data):
00043         """ Recursive helper method that transforms an object to XML.
00044 
00045             Returns a list of strings, which constitute the XML document.
00046         """
00047         # map tag names
00048         if self.TAG_MAP:
00049             element = self.TAG_MAP.get(element, element)
00050 
00051         if element:
00052             for prefix in self.PRIVATE_PREFIXES:
00053                 if element.startswith(prefix):
00054                     return ''
00055             content = ['<%s>' % element]
00056         else:
00057             content = []
00058 
00059         # Handle the different data types
00060         add_content = content.extend
00061 
00062         if data is None:
00063             content = "<none/>"
00064 
00065         elif isinstance(data, str):
00066             content = (data.replace("&", "&amp;") # Must be done first!
00067                            .replace("<", "&lt;")
00068                            .replace(">", "&gt;"))
00069 
00070         elif isinstance(data, dict):
00071             for key, value in data.items():
00072                 add_content(self.__toXML(key, value))
00073 
00074         elif isinstance(data, (list, tuple)):
00075             for item in data:
00076                 add_content(self.__toXML(self.ITEM_CONTAINER, item))
00077 
00078         elif hasattr(data, "toXML"):
00079             add_content([data.toXML()])
00080 
00081         elif hasattr(data, "__dict__"):
00082             add_content(self.__toXML(self.ROOT_CONTAINER, data.__dict__))
00083 
00084         else:
00085             content = (str(data).replace("&", "&amp;") # Must be done first!
00086                                 .replace("<", "&lt;")
00087                                 .replace(">", "&gt;"))
00088 
00089         # Close container element
00090         if isinstance(content, str):
00091             # No Whitespace
00092             if element:
00093                 content = ['<%s>%s</%s>' % (element, content, element)]
00094             else:
00095                 content = [content]
00096         elif element:
00097             # Whitespace
00098             content.append('</%s>' % element)
00099 
00100         return content
00101 
00102 
00103     def toXML(self):
00104         """ Transform an instance to an XML string.
00105         """
00106         return '\n'.join(self.__toXML(self.ROOT_CONTAINER, self.__dict__))
00107