2015-11-06 108 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'既不。 –