diff --git a/product/Formulator/FormToXML.py b/product/Formulator/FormToXML.py
index cae794308240e616f88d88dbf3800e92ad1b451a..a8f52de3fb4ab4cebf88860f7e3be9cc5604a4c2 100644
--- a/product/Formulator/FormToXML.py
+++ b/product/Formulator/FormToXML.py
@@ -1,85 +1,78 @@
-from StringIO import StringIO
+# -*- coding: utf-8 -*-
 from cgi import escape
-import types
+from lxml import etree
+from lxml.etree import Element, SubElement, CDATA
+from lxml.builder import E
 
-#def write(s):
-#    if type(s) == type(u''):
-#        print "Unicode:", repr(s)
-    
 def formToXML(form, prologue=1):
     """Takes a formulator form and serializes it to an XML representation.
     """
-    f = StringIO()
-    write = f.write
-
-    if prologue:
-        write('<?xml version="1.0"?>\n\n')
-    write('<form>\n')
+    form_as_xml = Element('form')
     # export form settings
     for field in form.settings_form.get_fields(include_disabled=1):
-        id = field.id
-        value = getattr(form, id)
-        if id == 'title':
-            value = escape(value)
-        if id == 'unicode_mode':
-            if value:
-                value = 'true'
-            else:
-                value = 'false'
-        write('  <%s>%s</%s>\n' % (id, value, id))
+      id = field.id
+      value = getattr(form, id)
+      if id == 'unicode_mode':
+        if value:
+          value = 'true'
+        else:
+          value = 'false'
+      sub_element = SubElement(form_as_xml, id)
+      sub_element.text = escape(str(value))
+    groups = SubElement(form_as_xml, 'groups')
     # export form groups
-    write('  <groups>\n')
     for group in form.get_groups(include_empty=1):
-        write('    <group>\n')
-        write('      <title>%s</title>\n' % escape(group))
-        write('      <fields>\n\n')
-        for field in form.get_fields_in_group(group, include_disabled=1):
-            write('      <field><id>%s</id> <type>%s</type>\n' % (field.id, field.meta_type))
-            write('        <values>\n')
-            items = field.values.items()
-            items.sort()
-            for key, value in items:
-                if value is None:
-                    continue
-                if value==True: # XXX Patch
-                    value = 1 # XXX Patch
-                if value==False: # XXX Patch
-                    value = 0 # XXX Patch
-                if callable(value): # XXX Patch
-                    write('          <%s type="method">%s</%s>\n' % # XXX Patch
-                        (key, escape(str(value.method_name)), key)) # XXX Patch
-                elif type(value) == type(1.1):
-                    write('          <%s type="float">%s</%s>\n' % (key, escape(str(value)), key))
-                elif type(value) == type(1):
-                    write('          <%s type="int">%s</%s>\n' % (key, escape(str(value)), key))
-                elif type(value) == type([]):
-                    write('          <%s type="list">%s</%s>\n' % (key, escape(str(value)), key))
-                else:
-                    if type(value) not in (types.StringType, types.UnicodeType):
-                        value = str(value)
-                    write('          <%s>%s</%s>\n' % (key, escape(value), key))
-            write('        </values>\n')
+      group_element = SubElement(groups, 'group')
+      group_element.append(E.title(group))
 
-            write('        <tales>\n')
-            items = field.tales.items()
-            items.sort()
-            for key, value in items:
-                if value:
-                    write('          <%s>%s</%s>\n' % (key, escape(str(value._text)), key))
-            write('        </tales>\n')
+      fields = SubElement(group_element, 'fields')
+      for field in form.get_fields_in_group(group, include_disabled=1):
+        field_element = E.field(
+                                  E.id(str(field.id)),
+                                  E.type(str(field.meta_type))
+                               )
 
-            write('        <messages>\n')
-            for message_key in field.get_error_names():
-                write('          <message name="%s">%s</message>\n' %
-                      (escape(message_key), escape(field.get_error_message(message_key))))
-            write('        </messages>\n')
-            write('      </field>\n')
-        write('      </fields>\n')
-        write('    </group>\n')
-    write('  </groups>\n')
-    write('</form>')
+        fields.append(field_element)
+        values_element = SubElement(field_element, 'values')
+        items = field.values.items()
+        items.sort()
+        for key, value in items:
+          if value is None:
+            continue
+          if value is True: # XXX Patch
+            value = 1 # XXX Patch
+          if value is False: # XXX Patch
+            value = 0 # XXX Patch
+          if callable(value): # XXX Patch
+            value_element = SubElement(values_element, key, type='method')
+          elif isinstance(value, float):
+            value_element = SubElement(values_element, key, type='float')
+          elif isinstance(value, int):
+            value_element = SubElement(values_element, key, type='int')
+          elif isinstance(value, list):
+            value_element = SubElement(values_element, key, type='list')
+          else:
+            if not isinstance(value, (str, unicode)):
+              value = str(value)
+            value_element = SubElement(values_element, key)
+          value_element.text = escape(str(value))
 
+          tales_element = SubElement(field_element, 'tales')
+          items = field.tales.items()
+          items.sort()
+          for key, value in items:
+            if value:
+              tale_element = SubElement(tales_element, key)
+              tale_element.text = escape(str(value._text))
+          messages = SubElement(field_element, 'messages')
+          for message_key in field.get_error_names():
+            message_element = SubElement(messages, 'message', name=message_key)
+            message_element.text = escape(field.get_error_message(message_key))
+    form_as_string = etree.tostring(form_as_xml, encoding='utf-8',
+                                    xml_declaration=True, pretty_print=True)
     if form.unicode_mode:
-        return f.getvalue().encode('UTF-8')
+      return etree.tostring(form_as_xml, encoding='utf-8',
+                                    xml_declaration=True, pretty_print=True)
     else:
-        return unicode(f.getvalue(), form.stored_encoding).encode('UTF-8')
+      return etree.tostring(form_as_xml, encoding=form.stored_encoding,
+                                    xml_declaration=True, pretty_print=True)
diff --git a/product/Formulator/XMLObjects.py b/product/Formulator/XMLObjects.py
index b8f1185a62785a037d1c176f94d9aa1b6409dc9e..4e33af9b337dc84aa4eac3563277b8b3c38b7f24 100644
--- a/product/Formulator/XMLObjects.py
+++ b/product/Formulator/XMLObjects.py
@@ -1,5 +1,6 @@
+# -*- coding: utf-8 -*-
 from xml.dom.minidom import parse, parseString, Node
-
+from xml.sax.saxutils import unescape
 # an extremely simple system for loading in XML into objects
 
 class Object:
@@ -40,7 +41,7 @@ def attributeToObject(parent, node):
 
 def textToObject(parent, node):
     # add this text to parents text content
-    parent.text += node.data
+    parent.text += unescape(node.data)
     
 def processingInstructionToObject(parent, node):
     # don't do anything with these
diff --git a/product/Formulator/tests/testSerializeForm.py b/product/Formulator/tests/testSerializeForm.py
index c23f89b220ead8ece7a0a38c07ab5c0345082e6d..0a4b32b0aaf461c41786b938dec624a0d73159a7 100644
--- a/product/Formulator/tests/testSerializeForm.py
+++ b/product/Formulator/tests/testSerializeForm.py
@@ -165,7 +165,7 @@ class SerializeTestCase(unittest.TestCase):
         form.manage_addField('multi_field', '<Checkbox> Field', 'MultiCheckBoxField')
 
         form2 = ZMIForm('test2', 'ValueTest')
-        
+
         xml = formToXML(form)
         XMLToForm(xml, form2)