2015-11-06 171 views
8

我想在操作XML时尽可能忠实地保留注释。在Parsed XML中保留注释(Python 2.7)

我设法保留评论,但内容得到XML转义。

#!/usr/bin/env python 
# add_host_to_tomcat.py 

import xml.etree.ElementTree as ET 
from CommentedTreeBuilder import CommentedTreeBuilder 
parser = CommentedTreeBuilder() 

if __name__ == '__main__': 
    filename = "/opt/lucee/tomcat/conf/server.xml" 

    # this is the important part: use the comment-preserving parser 
    tree = ET.parse(filename, parser) 

    # get the node to add a child to 
    engine_node = tree.find("./Service/Engine") 

    # add a node: Engine.Host 
    host_node = ET.SubElement(
     engine_node, 
     "Host", 
     name="local.mysite.com", 
     appBase="webapps" 
    ) 
    # add a child to new node: Engine.Host.Context 
    ET.SubElement(
     host_node, 
     'Context', 
     path="", 
     docBase="/path/to/doc/base" 
    ) 

    tree.write('out.xml') 
#!/usr/bin/env python 
# CommentedTreeBuilder.py 

from xml.etree import ElementTree 

class CommentedTreeBuilder (ElementTree.XMLTreeBuilder): 
    def __init__ (self, html = 0, target = None): 
     ElementTree.XMLTreeBuilder.__init__(self, html, target) 
     self._parser.CommentHandler = self.handle_comment 

    def handle_comment (self, data): 
     self._target.start(ElementTree.Comment, {}) 
     self._target.data(data) 
     self._target.end(ElementTree.Comment) 

然而,像这样的评论:

<!-- 
EXAMPLE HOST ENTRY: 
    <Host name="lucee.org" appBase="webapps"> 
     <Context path="" docBase="/var/sites/getrailo.org" /> 
    <Alias>www.lucee.org</Alias> 
    <Alias>my.lucee.org</Alias> 
    </Host> 

HOST ENTRY TEMPLATE: 
    <Host name="[ENTER DOMAIN NAME]" appBase="webapps"> 
     <Context path="" docBase="[ENTER SYSTEM PATH]" /> 
    <Alias>[ENTER DOMAIN ALIAS]</Alias> 
    </Host> 
    --> 

最终成为:

<!-- 
      EXAMPLE HOST ENTRY: 
    &lt;Host name="lucee.org" appBase="webapps"&gt; 
     &lt;Context path="" docBase="/var/sites/getrailo.org" /&gt; 
     &lt;Alias&gt;www.lucee.org&lt;/Alias&gt; 
     &lt;Alias&gt;my.lucee.org&lt;/Alias&gt; 
    &lt;/Host&gt; 

    HOST ENTRY TEMPLATE: 
    &lt;Host name="[ENTER DOMAIN NAME]" appBase="webapps"&gt; 
     &lt;Context path="" docBase="[ENTER SYSTEM PATH]" /&gt; 
     &lt;Alias&gt;[ENTER DOMAIN ALIAS]&lt;/Alias&gt; 
    &lt;/Host&gt; 
    --> 

我也试过CommentedTreeBuilder.pyself._target.data(saxutils.unescape(data)),但它似乎并没有做任何事情。事实上,我认为问题发生在handle_commment()之后的某个地方。

顺便提一下,这个问题类似于this

回答

8

经过Python 2.7和3.5的测试,下面的代码应该按预期工作。

#!/usr/bin/env python 
# CommentedTreeBuilder.py 
from xml.etree import ElementTree 

class CommentedTreeBuilder(ElementTree.TreeBuilder): 
    def __init__(self, *args, **kwargs): 
     super(CommentedTreeBuilder, self).__init__(*args, **kwargs) 

    def comment(self, data): 
     self.start(ElementTree.Comment, {}) 
     self.data(data) 
     self.end(ElementTree.Comment) 

然后,在主代码使用

parser = ET.XMLParser(target=CommentedTreeBuilder()) 

作为解析器,而不是当前的。

顺便说一下,注释与lxml开箱即可正常使用。也就是说,你可以做

import lxml.etree as ET 
tree = ET.parse(filename) 

不需要任何上述。

+0

这两个解决方案似乎都保留了评论,谢谢!但其他元素会被重新格式化(可能会重新排序属性)。我知道这对于机器可读性无关紧要,但对于我的目的(人的可读性,版本控制以及只触及明确触动的元素)而言,这很重要。 FWIW,我的原始版本恰好使其他元素保持不变(即格式化为原始版本)。这个答案确实解决了我明确的问题,所以它会得到答案奖,但我想知道是否也可以使用非注释元素格式保存。 –

+0

就我所见,唯一被修改的东西是属性的排序和标签内的空白(如果我遗漏了任何东西,请纠正我)。你通常不应该关心后者。由于属性存储在'xml'的字典中,它们的排序在输出中是随机的。要解决这个问题,您可以使用类似于注释的解决方法,请参阅http://stackoverflow.com/q/2741480/2997179。或者用'lxml'快速测试显示,它似乎保留了属性顺序,似乎是一个可行的解决方案。无论哪种方式,我认为这值得单独的SO问题。 –

+0

好吧..在属性之上,'xml'也放弃了处理指令标记(例如'<?xml-stylesheet href =“mystyle.css”type =“text/css”?>'),并重命名xmlns命名空间。再次,'lxml'既不。 –