Python 在解析的XML中忠实地保留注释

Python 在解析的XML中忠实地保留注释,python,xml,python-2.7,elementtree,Python,Xml,Python 2.7,Elementtree,我希望在处理XML时尽可能忠实地保留注释 我设法保留了注释,但内容正在被XML转义 #/usr/bin/env python #将\u主机\u添加到\u tomcat.py 将xml.etree.ElementTree作为ET导入 从CommentedTreeBuilder导入CommentedTreeBuilder parser=CommentedTreeBuilder() 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': filename=“/opt/l

我希望在处理XML时尽可能忠实地保留注释

我设法保留了注释,但内容正在被XML转义

#/usr/bin/env python
#将\u主机\u添加到\u tomcat.py
将xml.etree.ElementTree作为ET导入
从CommentedTreeBuilder导入CommentedTreeBuilder
parser=CommentedTreeBuilder()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
filename=“/opt/lucee/tomcat/conf/server.xml”
#这是重要的部分:使用注释保留解析器
tree=ET.parse(文件名,解析器)
#获取要向其中添加子节点的节点
引擎节点=树。查找(“./服务/引擎”)
#添加一个节点:Engine.Host
host_node=ET.SubElement(
引擎节点,
“主持人”,
name=“local.mysite.com”,
appBase=“webapps”
)
#将子节点添加到新节点:Engine.Host.Context
等子元素(
主机节点,
“上下文”,
路径=”,
docBase=“/path/to/doc/base”
)
write('out.xml')
#/usr/bin/env python
#CommentedTreeBuilder.py
从xml.etree导入元素树
类CommentedTreeBuilder(ElementTree.XMLTreeBuilder):
定义初始化(self,html=0,target=None):
ElementTree.XMLTreeBuilder.\uuuuu init\uuuuu(self、html、target)
self.\u parser.CommentHandler=self.handle\u comment
def句柄注释(自身、数据):
self.\u target.start(ElementTree.Comment,{})
自身目标数据(数据)
self.\u target.end(ElementTree.Comment)
然而,评论如下:


使用Python 2.7和3.5进行测试,下面的代码应该可以正常工作

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

class CommentedTreeBuilder(ElementTree.TreeBuilder):
    def comment(self, data):
        self.start(ElementTree.Comment, {})
        self.data(data)
        self.end(ElementTree.Comment)
import xml.etree.ElementTree as ET

class CommentedTreeBuilder(ET.XMLTreeBuilder):
    def __init__(self, *args, **kwargs):
        super(CommentedTreeBuilder, self).__init__(*args, **kwargs)
        self._parser.CommentHandler = self.comment

    def comment(self, data):
        self._target.start(ET.Comment, {})
        self._target.data(data)
        self._target.end(ET.Comment)
然后,在主代码中使用

parser = ElementTree.XMLParser(target=CommentedTreeBuilder())
作为解析器而不是当前解析器

顺便说一下,使用
lxml
,注释可以在开箱即用的情况下正常工作。也就是说,你可以这样做

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

不需要上述任何一项。

马丁的代码对我不起作用。我用下面的代码修改了相同的代码,它可以按预期的方式工作

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

class CommentedTreeBuilder(ElementTree.TreeBuilder):
    def comment(self, data):
        self.start(ElementTree.Comment, {})
        self.data(data)
        self.end(ElementTree.Comment)
import xml.etree.ElementTree as ET

class CommentedTreeBuilder(ET.XMLTreeBuilder):
    def __init__(self, *args, **kwargs):
        super(CommentedTreeBuilder, self).__init__(*args, **kwargs)
        self._parser.CommentHandler = self.comment

    def comment(self, data):
        self._target.start(ET.Comment, {})
        self._target.data(data)
        self._target.end(ET.Comment)
这就是测试

    parser=CommentedTreeBuilder()
    tree = ET.parse(filename, parser)
    tree.write('out.xml')

看起来来自@Martin和@sukhbinder的两个答案对我都不起作用。。。因此,在Python3.x上,这是一个可行的完整解决方案

from xml.etree import ElementTree

string = '''<?xml version="1.0"?>
<data>
    <!--Test
    -->
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
</data>'''

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

parser = ElementTree.XMLParser(target=CommentedTreeBuilder())
tree = ElementTree.fromstring(string, parser)
print(tree.find("./*[0]").text)
# or ElementTree.parse(filename, parser)
从xml.etree导入元素树
字符串=“”
1.
2008
141100
'''
类CommentedTreeBuilder(ElementTree.TreeBuilder):
def注释(自我、数据):
self.start(ElementTree.Comment,{})
自我数据(数据)
self.end(ElementTree.Comment)
parser=ElementTree.XMLParser(target=CommentedTreeBuilder())
tree=ElementTree.fromstring(字符串,解析器)
打印(tree.find(“./*[0]”)文本)
#或ElementTree.parse(文件名,解析器)
TreeBuilder
中添加了
insert\u comments
参数,其中:

类xml.etree.ElementTree.TreeBuilder(element\u factory=None,*,comment\u factory=None,pi\u factory=None,insert\u comments=False,insert\u pis=False)

当insert_comments和/或insert_pis为true时,如果注释/pis出现在根元素内(而不是根元素外),则它们将被插入到树中

例如:

parser = ElementTree.XMLParser(target=ElementTree.TreeBuilder(insert_comments=True))

Martin的答案是正确的只是缺少一些代码, 我知道这对更有经验的编程人员来说是显而易见的,但作为一名新的编程人员,我花了一分钟时间才明白: 马汀的回答是:

import xml.etree.ElementTree as ET
from xml.etree import ElementTree

class CommentedTreeBuilder(ElementTree.TreeBuilder):
# This class will retain remarks and comments oposed to the xml parser default
    def comment(self, data):
        self.start(ElementTree.Comment, {})
        self.data(data)
        self.end(ElementTree.Comment)

# the missing part:
def parse_xml_with_remarks(filepath):
    ctb = CommentedTreeBuilder()
    xp = ET.XMLParser(target=ctb)
    tree = ET.parse(filepath, parser=xp)
    return tree

# parsing the file, and getting root
tree=parse_xml_with_remarks(file)
root=tree.getroot()

两种解决方案似乎都保留了评论,谢谢!但其他元素会被重新格式化(属性可能会被重新排序)。我知道这与机器可读性无关,但对于我的目的(人类可读性、版本控制和只接触显式接触的元素),这很重要。FWIW,我的原始版本恰好保留了其他元素的完整性(即格式与原始版本相同)。这个答案确实解决了我的明确问题,因此它将获得答案奖,但我想知道是否也可以保留非注释元素格式。据我所知,唯一需要修改的是属性的顺序和标记内的空格(如果我缺少任何内容,请更正我)。你通常不应该拥有或关心后者。由于属性存储在
xml
的字典中,因此它们在输出中的顺序是随机的。要解决这个问题,您可以使用类似于注释的解决方法,请参阅。或者,正如使用
lxml
进行的快速测试所示,它似乎保留了属性顺序,似乎是一个可行的解决方案。不管怎样,我认为这应该是一个单独的问题。好吧。。在属性之上,
xml
还丢弃处理指令标记(例如,
)并重命名xmlns名称空间。同样,
lxml
两者都不做。默认情况下,
xml
lxml
也会省略
声明。使用Python 3.5.4,这在Python 3.8中要容易得多。看,这是Python3.52及以上版本,为我使用Python3.8工作