geekdoc-python-zh/docs/pythonlibrary/python-creating-xml-with-lx...

8.7 KiB
Raw Permalink Blame History

Python:用 lxml.objectify 创建 XML

原文:https://www.blog.pythonlibrary.org/2014/03/26/python-creating-xml-with-lxml-objectify/

lxml.objectify 子包对于解析和创建 xml 非常方便。在本文中,我们将展示如何使用 lxml 包创建 XML。我们将从一些简单的 XML 开始,然后尝试复制它。我们开始吧!

在过去的篇文章中,我使用了以下愚蠢的 XML 示例进行演示:


 <appointment><begin>1181251680</begin>
        <uid>040000008200E000</uid>
        <alarmtime>1181572063</alarmtime>
        <state><location><duration>1800</duration>
        <subject>Bring pizza home</subject></location></state></appointment> 
    <appointment><begin>1234360800</begin>
        <duration>1800</duration>
        <subject>Check MS Office website for updates</subject>
        <location><uid>604f4792-eb89-478b-a14f-dd34d3cc6c21-1234360800</uid>
        <state>dismissed</state></location></appointment> 

让我们看看如何使用 lxml.objectify 来重新创建这个 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()

让我们把它分解一下。我们将从 create_xml 函数开始。在其中,我们使用 objectify 模块的 fromstring 函数创建了一个 XML 根对象。根对象将包含 zAppointment 作为它的标签。我们设置根的提醒属性,然后使用字典作为参数调用我们的 create_appt 函数。在 create_appt 函数中,我们创建了一个元素的实例(从技术上讲,它是一个 ObjectifiedElement ),我们将它分配给了我们的 appt 变量。这里我们使用点符号来创建这个元素的标签。最后,我们返回 appt 元素并将其附加到我们的根对象中。我们对第二个约会实例重复这个过程。

create_xml 函数的下一部分将删除 lxml 注释。如果您不这样做,您的 XML 将看起来像下面这样:


 <appointment py:pytype="TREE"><begin py:pytype="int">1181251680</begin>
        <uid py:pytype="str">040000008200E000</uid>
        <alarmtime py:pytype="int">1181572063</alarmtime>
        <state py:pytype="str"><location py:pytype="str"><duration py:pytype="int">1800</duration>
        <subject py:pytype="str">Bring pizza home</subject></location></state></appointment> <appointment py:pytype="TREE"><begin py:pytype="int">1234360800</begin>
        <uid py:pytype="str">604f4792-eb89-478b-a14f-dd34d3cc6c21-1234360800</uid>
        <alarmtime py:pytype="int">1181572063</alarmtime>
        <state py:pytype="str">dismissed</state>
        <location py:pytype="str"><duration py:pytype="int">1800</duration>
        <subject py:pytype="str">Check MS Office website for updates</subject></location></appointment> 

为了删除所有不需要注释,我们调用以下两个函数:


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

难题的最后一部分是让 lxml 自己生成 xml。这里我们使用 lxml 的 etree 模块来完成这项艰巨的工作:


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

tostring 函数将返回一个漂亮的 XML 字符串,如果您将 pretty_print 设置为 True它通常也会以漂亮的格式返回 XML。

Python 3 的 2020 年 11 月更新

在 Python 3 中上一节中的代码没有将“修饰过的”XML 输出到文件中。要使它正常工作,你必须经历更多的困难。这里是一个更新版本的代码。在 Mac OS 上的 Python 3.9 中测试:


from lxml import etree, objectify
from io import BytesIO

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
    parser = etree.XMLParser(remove_blank_text=True)
    file_obj = BytesIO(etree.tostring(root))
    tree = etree.parse(file_obj, parser)

    try:
        with open("example.xml", "wb") as xml_writer:
            tree.write(xml_writer, pretty_print=True)
    except IOError:
        pass

if __name__ == "__main__":
    create_xml()

这是基于在 StackOverflow 上找到的解决方案。

您需要在文件的顶部添加一个新的导入来获得字节数。然后在代码的结尾,你需要修改你的代码,看起来像这样:


# create the xml string
parser = etree.XMLParser(remove_blank_text=True)
file_obj = BytesIO(etree.tostring(root))
tree = etree.parse(file_obj, parser)

try:
    with open("example.xml", "wb") as xml_writer:
        tree.write(xml_writer, pretty_print=True)
except IOError:
    pass

这将添加一个新的 XML 解析器对象,从根目录中删除空白文本。这将发生在你把根变成一个字节串,而这个字节串本身又用 BytesIO 变成一个类似文件的对象之后。试一试,您应该得到一个包含适当缩进的 XML 代码的文件。


包扎

现在您知道了如何使用 lxml 的 objectify 模块来创建 xml。这是一个非常方便的界面并且在很大程度上相当 Pythonic 化。


相关阅读