"""
XMLTemplate: Simple XML template processor
Purpose:
Transform one XML document into another. Could
put the V in MVC for a web app, for instance.
Usage:
Define a class whose member functions have
the signature: def func(self, attrs, contents)
where attrs is a dictionary and contents is
a string (corresponds with an xml tag like
"<func attrs>contents</func>"). The member
should return a string which will replace
the entire tag. Tags with no corresponding
member function pass through unharmed.
See the __main__ section below for an example.
from XMLTemplate import XMLTemplate
class MyTemplate(): #Class to define tag replacements
...
template = XMLTemplate(MyTemplate()) #Prepare template
template.parse('<doc><foo a="1" /></doc>') #Parse XML String
print template #Output result
template.parse('<doc></doc>') #Reuse object for next template
print template #Output new result
"""
__author__ = "Patrick Lioi <patrick_at_lioi_dot_net>"
__date__ = "2003/09/13 3:50:01"
__version__ = "1.0"
from xml.sax import make_parser
from xml.sax.handler import ContentHandler
from StringIO import StringIO
class XMLTemplate(ContentHandler):
def __init__(self, tagHandler):
self.tagHandler = tagHandler
def __reset(self):
self.stack = [{"attrs":{},"contents":[]}]
def parse(self, xml):
self.__reset()
parser = make_parser()
parser.setContentHandler(self)
parser.parse(StringIO(xml))
def __repr__(self):
#After parsing, the stack contains one element,
#which is a list of strings that must be joined
return "".join(self.stack[-1]["contents"])
def __createTag(self, name, attrs, contents):
attrib_str = "".join(
[' %s="%s"'%(key, value)
for key, value in attrs.items()])
if len(contents)==0:
return "<%s%s />"%(name, attrib_str)
else:
return "<%s%s>%s</%s>"%(name, attrib_str, contents, name)
def startElement(self, name, attrs):
#Push space for this tag onto the stack
self.stack.append({"attrs":attrs,"contents":[]})
def characters (self, ch):
#Append characters to the top stack element
self.stack[-1]["contents"].append(ch)
def endElement(self, name):
data = self.stack.pop()
if hasattr(self.tagHandler, name):
member = getattr(self.tagHandler, name)
buffer = member(data["attrs"], "".join(data["contents"]))
else:
buffer = self.__createTag(name, data["attrs"], "".join(data["contents"]))
#Propogate this processed tag down the stack
sublevel = self.stack.pop()
sublevel["contents"].append(buffer)
self.stack.append(sublevel)
if __name__ == "__main__":
class MyTemplate:
def name(self, attrs, contents):
return "Patrick Lioi"
def link(self, attrs, contents):
return """<a href="%s">%s</a>"""%(attrs["address"], contents)
TEMPLATE = """
<html>
<body>
<link address="http://patrick.lioi.net/"><name /></link>
</body>
</html>"""
template = XMLTemplate(MyTemplate())
template.parse(TEMPLATE)
print template