如何使用ElementTree输出CDATA
我发现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")
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")