我发现cElementTree的速度比xml.dom.minidom
快30倍,我正在重写我的XML编码/解码代码。但是,我需要输出包含CDATA部分的XML,并且似乎没有办法用ElementTree来实现这一点。如何使用ElementTree输出CDATA
可以这样做吗?
我发现cElementTree的速度比xml.dom.minidom
快30倍,我正在重写我的XML编码/解码代码。但是,我需要输出包含CDATA部分的XML,并且似乎没有办法用ElementTree来实现这一点。如何使用ElementTree输出CDATA
可以这样做吗?
经过一番工作,我自己找到了答案。查看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部分再输出它,这仍然没有帮助,但它至少允许你以编程方式用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")
这不可能AFAIK ...这是可惜的。基本上,ElementTree模块假设读者是100%符合XML的,所以他们输出一段作为CDATA或其他生成等效文本的格式应该没有关系。
请参阅Python邮件列表上的this thread以获取更多信息。基本上,他们推荐使用某种基于DOM的XML库。
我不会称之为“可惜”。对于XML信息集(内容),“<![CDATA [&]]>”和“&”之间没有区别......大多数XML解析器甚至不会让您知道原始文档中的内容。 – bortzmeyer 2008-10-15 12:13:55
确实如此,但是一些数据可以在CDATA格式中更加高效地转储和分析。因此,无法通过这种方式告诉XML库来处理它是一件痛苦的事情。 – 2008-10-15 21:02:26
其实这个代码有缺陷,因为你不赶]]>
出现在数据要插入的CDATA
按Is there a way to escape a CDATA end token in xml?
则应将其拆分为两个CDATA在这种情况下,分裂两者之间的]]>
。
基本上data = data.replace("]]>", "]]]]><![CDATA[>")
(不一定正确,请确认)
在DOM(ATLEAST在第2级)的接口 DATASection和操作文档:: createCDATASection。它们是 扩展接口,仅当实现支持 “xml”功能时才受支持。
从进口xml.dom的minidom命名
my_xmldoc = minidom命名。解析(XMLFILE)
my_xmldoc.createCDATASection(数据)
现在u有cadata节点添加它哪里ü希望....
这里是gooli的解决方案的一个变体使用Python 3.2的工作原理:
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")
这是我的版本,它基于上面的gooli和amaury的答案。它适用于ElementTree 1.2.6和1.3.0,它们使用完全不同的方法。
请注意,gooli's不适用于1.3.0,这似乎是Python 2.7.x中的当前标准。
另请注意,此版本不使用CDATA()方法使用gooli。
import xml.etree.cElementTree as ET
class ElementTreeCDATA(ET.ElementTree):
"""Subclass of ElementTree which handles CDATA blocks reasonably"""
def _write(self, file, node, encoding, namespaces):
"""This method is for ElementTree <= 1.2.6"""
if node.tag == '![CDATA[':
text = node.text.encode(encoding)
file.write("\n<![CDATA[%s]]>\n" % text)
else:
ET.ElementTree._write(self, file, node, encoding, namespaces)
def _serialize_xml(write, elem, qnames, namespaces):
"""This method is for ElementTree >= 1.3.0"""
if elem.tag == '![CDATA[':
write("\n<![CDATA[%s]]>\n" % elem.text)
else:
ET._serialize_xml(write, elem, qnames, namespaces)
我到了这里寻找一种方法来“用CDATA部分解析XML然后再用CDATA部分输出它”。
我能够做到这一点(也许lxml已经从这篇文章更新?)与以下:(这是一个有点粗糙 - 对不起;-)。其他人可能有更好的方式来编程式地找到CDATA部分,但我太懒惰了。
parser = etree.XMLParser(encoding='utf-8') # my original xml was utf-8 and that was a lot of the problem
tree = etree.parse(ppath, parser)
for cdat in tree.findall('./ProjectXMPMetadata'): # the tag where my CDATA lives
cdat.text = etree.CDATA(cdat.text)
# other stuff here
tree.write(opath, encoding="UTF-8",)
这最终在Python 2.7中为我工作。类似于阿毛里的回答。
import xml.etree.ElementTree as ET
ET._original_serialize_xml = ET._serialize_xml
def _serialize_xml(write, elem, encoding, qnames, namespaces):
if elem.tag == '![CDATA[':
write("<%s%s]]>%s" % (elem.tag, elem.text, elem.tail))
return
return ET._original_serialize_xml(
write, elem, encoding, qnames, namespaces)
ET._serialize_xml = ET._serialize['xml'] = _serialize_xml
我发现一个黑客获得CDATA使用注释工作:
node.append(etree.Comment(' --><![CDATA[' + data.replace(']]>', ']]]]><![CDATA[>') + ']]><!-- '))
我不知道的建议代码以前的版本是否取得了良好效果是否ElementTree的模块已更新,但我所面临的问题与使用此招:
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
这种方法的问题是,通过这个异常后,串行再次把它当作普通的标签后秒。我得到这样的东西:
<textContent>
<![CDATA[this was the code I wanted to put inside of CDATA]]>
<![CDATA[>this was the code I wanted to put inside of CDATA</![CDATA[>
</textContent>
当然,我们知道这只会导致大量的错误。 这是为什么发生的?
答案就在这个小家伙:
return etree._original_serialize_xml(write, elem, qnames, namespaces)
我们不想通过原还原序列化功能再次检查代码,如果我们已经被困了CDATA并顺利通过它通过。 因此,在“if”块中,只有当CDATA不存在时,我们才能返回原始序列化函数。在返回原始函数之前,我们错过了“其他”。
此外在我的版本ElementTree模块中,serialize函数拼命地要求“short_empty_element”参数。因此,最新的版本,我会建议这个样子的(也与“尾巴”):
from xml.etree import ElementTree
from xml import etree
#in order to test it you have to create testing.xml file in the folder with the script
xmlParsedWithET = ElementTree.parse("testing.xml")
root = xmlParsedWithET.getroot()
def CDATA(text=None):
element = ElementTree.Element('![CDATA[')
element.text = text
return element
ElementTree._original_serialize_xml = ElementTree._serialize_xml
def _serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs):
if elem.tag == '![CDATA[':
write("\n<{}{}]]>\n".format(elem.tag, elem.text))
if elem.tail:
write(_escape_cdata(elem.tail))
else:
return ElementTree._original_serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs)
ElementTree._serialize_xml = ElementTree._serialize['xml'] = _serialize_xml
text = """
<?xml version='1.0' encoding='utf-8'?>
<text>
This is just some sample text.
</text>
"""
e = ElementTree.Element("data")
cdata = CDATA(text)
root.append(cdata)
#tests
print(root)
print(root.getchildren()[0])
print(root.getchildren()[0].text + "\n\nyay!")
我得到的输出是:
<Element 'Database' at 0x10062e228>
<Element '![CDATA[' at 0x1021cc9a8>
<?xml version='1.0' encoding='utf-8'?>
<text>
This is just some sample text.
</text>
yay!
祝你相同的结果!
>我需要输出包含CDATA部分的XML 为什么?这似乎是一个奇怪的要求。 – bortzmeyer 2008-10-15 12:14:57
这是我的要求 - CDATA块有时更易读。 – grifaton 2010-09-06 22:39:35
@bortzmeyer将它添加到KML(Google Maps XML文件)非常有用。 – 2016-06-23 11:59:31