31st May, 2008

Serialise Python objects to XML

I'm lazy. I like a complete separation of back-end code and front end styling - logic in the back end, styling for front end. There are many templating solutions floating around, but most put code in with markup. Fortunately, we have a templating solution which most web developers already know... XSL.

The drawback to this is you need an XML document to transform. You have to create this document, populate it with every value the transform might possibly need, load up the XSL, transform it, output it... it's boooring!

So I created a transform function which takes an XML document and transforms it. Ace. Except you need the XML document to transform, and this XML document needs to be created easily.

The absolutely laziest way I can think is by generating XML from your Python object model. This is work in progress but still a working function which will return a valid XML document based on any Python object you pass in.

Have a look at the simple XML object for the helper classes.

def ItemToXml(self, item, format = None, localvariables = None):
    item_type = item.__class__.__name__ 
    if item_type == "" or item_type == None:
        item_type = "'Unknown.Item'"

    itemName = item_type
    localBuffer = []

    if type(item) == type(int()):
        return cgi.escape(self.ItemToAscii(item))
    if type(item) == type(str()):
        return cgi.escape(item)
    if hasattr(item, "__iter__"):
        for __loop in item:
            localBuffer.append(
                self.ItemToXml(__loop)
            )
    try:
        guid = item.guid
    except Exception,er:
        pass

    xml = Xml.Writer()
    xml.StartDocument(itemName)

    if localvariables != None:
        xml.StartElement("locals")          
        for loop_key, loop_item in localvariables.items():
            xml.WriteElement(
                loop_key, self.ItemToAscii(loop_item)
            )
        xml.EndElement()
    for loopItem in dir(item):
        if loopItem[:1] != "_":
            try:
                loopItemValue = getattr(item, loopItem)
                if type(loopItemValue) == dict or type(loopItemValue) == tuple:
                    xml.StartElement(loopItem)
                    try:
                        for loopInnerItemKey, loopInnerItem in loopItemValue:                               
                            xml.WriteRaw(
                                self.ItemToXml(loopInnerItem, format)
                            )
                    except Exception, er:
                        pass
                    xml.EndElement()
                if type(loopItemValue) == list:
                    xml.StartElement(loopItem)
                    try:
                        for loopInnerItem in loopItemValue:
                            xml.WriteRaw(
                                self.ItemToXml(loopInnerItem, format)
                            )
                    except Exception, er:
                        pass
                    xml.EndElement()
                if not hasattr(loopItemValue, "__iter__"):
                    xml.WriteElement(
                        loopItem, self.ItemToAscii(loopItemValue)
                    )
                if format != None:
                    if format.has_key(loopItem):
                        value = self.ItemToXmlFormat(
                            loopItemValue, format[loopItem]
                        )
                        try:
                            xml.StartElement(loopItem)
                            xml.WriteAttribute(
                                "format",
                                format[loopItem]
                            )
                            xml.WriteRaw(value)
                            xml.EndElement()
                        except Exception, ex:
                            xml.EndElement()
                            xml.WriteElement(
                                "error",
                                str(ex)
                            )

                if type(loopItemValue) == type(datetime.now()):
                    xml.StartElement(loopItem)
                    xml.WriteAttribute(
                        "format",
                        "friendlydate"
                    )
                    xml.WriteContent(
                        self.FormatDate(
                            self.ItemToAscii(loopItemValue)
                        )
                    )
                    xml.EndElement()

                    xml.StartElement(loopItem)
                    xml.WriteAttribute(
                        "format",
                        "friendlydatetime"
                    )
                    xml.WriteContent(
                        self.FormatDateTime(
                            self.ItemToAscii(loopItemValue)
                        )
                    )
                    xml.EndElement()

            except Exception, er:
                self.context.Log(er)
    return xml.Output()

 

The opinions expressed here are my own and not those of my employer.