如何使用ElementTree输出CDATA

52 投票
16 回答
61648 浏览
提问于 2025-04-11 09:27

我发现cElementTree的速度比xml.dom.minidom快大约30倍,所以我正在重写我的XML编码和解码代码。不过,我需要输出包含CDATA部分的XML,而用ElementTree似乎没有办法做到这一点。

这能实现吗?

16 个回答

13

这是一个适用于Python 3.2的gooli解决方案的变体:

import xml.etree.ElementTree as etree

def CDATA(text=None):
    element = etree.Element('![CDATA[')
    element.text = text
    return element

etree._original_serialize_xml = etree._serialize_xml
def _serialize_xml(write, elem, qnames, namespaces):
    if elem.tag == '![CDATA[':
        write("\n<%s%s]]>\n" % (
                elem.tag, elem.text))
        return
    return etree._original_serialize_xml(
        write, elem, qnames, namespaces)
etree._serialize_xml = etree._serialize['xml'] = _serialize_xml


if __name__ == "__main__":
    import sys

    text = """
    <?xml version='1.0' encoding='utf-8'?>
    <text>
    This is just some sample text.
    </text>
    """

    e = etree.Element("data")
    cdata = CDATA(text)
    e.append(cdata)
    et = etree.ElementTree(e)
    et.write(sys.stdout.buffer.raw, "utf-8")
21

lxml 是一个库,它支持 CDATA,并且有类似 ElementTree 的接口。

29

经过一番努力,我自己找到了答案。查看ElementTree.py的源代码后,我发现它对XML注释和预处理指令有特别的处理。它们创建了一个工厂函数,用于处理特殊的元素类型,这个元素类型使用一种特殊的(非字符串)标签值来区分于普通元素。

def Comment(text=None):
    element = Element(Comment)
    element.text = text
    return element

然后在ElementTree的_write函数中,负责输出XML的部分,有一个专门处理注释的情况:

if tag is Comment:
    file.write("<!-- %s -->" % _escape_cdata(node.text, encoding))

为了支持CDATA部分,我创建了一个叫CDATA的工厂函数,扩展了ElementTree类,并修改了_write函数来处理CDATA元素。

不过,如果你想解析一个包含CDATA部分的XML,然后再输出它,仍然没有帮助,但至少这让你可以通过编程的方式创建包含CDATA部分的XML,这正是我需要做的。

这个实现似乎可以同时在ElementTree和cElementTree中工作。

import elementtree.ElementTree as etree
#~ import cElementTree as etree

def CDATA(text=None):
    element = etree.Element(CDATA)
    element.text = text
    return element

class ElementTreeCDATA(etree.ElementTree):
    def _write(self, file, node, encoding, namespaces):
        if node.tag is CDATA:
            text = node.text.encode(encoding)
            file.write("\n<![CDATA[%s]]>\n" % text)
        else:
            etree.ElementTree._write(self, file, node, encoding, namespaces)

if __name__ == "__main__":
    import sys

    text = """
    <?xml version='1.0' encoding='utf-8'?>
    <text>
    This is just some sample text.
    </text>
    """

    e = etree.Element("data")
    cdata = CDATA(text)
    e.append(cdata)
    et = ElementTreeCDATA(e)
    et.write(sys.stdout, "utf-8")

撰写回答