Python: Creating XML with lxml.objectify

The lxml.objectify sub-package is extremely handy for parsing and creating XML. In this article, we will show how to create XML using the lxml package. We’ll start with some simple XML and then try to replicate it. Let’s get started!

In past articles, I have used the following silly example XML for demonstration purposes:



    
        1181251680
        040000008200E000
        1181572063
        
        
        1800
        Bring pizza home
    
    
        1234360800
        1800
        Check MS Office website for updates
        
        604f4792-eb89-478b-a14f-dd34d3cc6c21-1234360800
        dismissed
  

Let’s see how we can use lxml.objectify to recreate this XML:

from lxml import etree, objectify

#----------------------------------------------------------------------
def create_appt(data):
    """
    Create an appointment XML element
    """
    appt = objectify.Element("appointment")
    appt.begin = data["begin"]
    appt.uid = data["uid"]
    appt.alarmTime = data["alarmTime"]
    appt.state = data["state"]
    appt.location = data["location"]
    appt.duration = data["duration"]
    appt.subject = data["subject"]
    return appt

#----------------------------------------------------------------------
def create_xml():
    """
    Create an XML file
    """
    
    xml = '''
    
    
    '''
        
    root = objectify.fromstring(xml)
    root.set("reminder", "15")
    
    appt = create_appt({"begin":1181251680,
                        "uid":"040000008200E000",
                        "alarmTime":1181572063,
                        "state":"",
                        "location":"",
                        "duration":1800,
                        "subject":"Bring pizza home"}
                       )
    root.append(appt)
    
    uid = "604f4792-eb89-478b-a14f-dd34d3cc6c21-1234360800"
    appt = create_appt({"begin":1234360800,
                        "uid":uid,
                        "alarmTime":1181572063,
                        "state":"dismissed",
                        "location":"",
                        "duration":1800,
                        "subject":"Check MS Office website for updates"}
                       )
    root.append(appt)
    
    # remove lxml annotation
    objectify.deannotate(root)
    etree.cleanup_namespaces(root)
    
    # create the xml string
    obj_xml = etree.tostring(root,
                             pretty_print=True,
                             xml_declaration=True)
    
    try:
        with open("example.xml", "wb") as xml_writer:
            xml_writer.write(obj_xml)
    except IOError:
        pass
    
#----------------------------------------------------------------------
if __name__ == "__main__":
    create_xml()

Let’s break this down a bit. We will start with the create_xml function. In it we create an XML root object using the objectify module’s fromstring function. The root object will contain zAppointment as its tag. We set the root’s reminder attribute and then we call our create_appt function using a dictionary for its argument. In the create_appt function, we create an instance of an Element (technically, it’s an ObjectifiedElement) that we assign to our appt variable. Here we use dot-notation to create the tags for this element. Finally we return the appt element back and append it to our root object. We repeat the process for the second appointment instance.

The next section of the create_xml function will remove the lxml annotation. If you do not do this, you’re XML will end up looking like the following:


    
        1181251680
        040000008200E000
        1181572063
        
        
        1800
        Bring pizza home
    
        1234360800
        604f4792-eb89-478b-a14f-dd34d3cc6c21-1234360800
        1181572063
        dismissed
        
        1800
        Check MS Office website for updates
    

To remove all that unwanted annotation, we call the following two functions:

objectify.deannotate(root)
etree.cleanup_namespaces(root)

The last piece of the puzzle is to get lxml to generate the the XML itself. Here we use lxml’s etree module to do the hard work:

obj_xml = etree.tostring(root, pretty_print=True)

The tostring function will return a nice string of the XML and if you set pretty_print to True, it will usually return the XML in a nice format too.


Wrapping Up

Now you know how to use lxml’s objectify module to create XML. It’s a pretty handy interface and quite Pythonic for the most part.


Related Reading