一尘不染

可以告诉ElementTree保留属性的顺序吗?

python

我使用ElementTree在python中编写了一个相当简单的过滤器,以调整某些xml文件的上下文。它或多或少地起作用。

但是它重新排序了各种标签的属性,我希望它不要这样做。

有谁知道我可以抛出的开关使其保持在指定的顺序?

上下文

我正在使用一个粒子物理工具,该工具具有基于xml文件的复杂但奇怪的配置系统。以这种方式设置的许多事物中包括各种静态数据文件的路径。这些路径被硬编码到现有的xml中,没有基于环境变量设置或更改它们的功能,在我们的本地安装中,它们必须位于不同的位置。

这不是灾难,因为我们使用的源代码控制和构建控制工具相结合,使我们可以使用本地副本对某些文件进行阴影处理。但是,即使以为数据字段是静态的,xml也不是静态的,所以我写了一个脚本来修复路径,但是由于属性重排,本地版本和主版本之间的差异很难理解,因此不必要。


这是我第一次带ElementTree旋转(而且只有我的第五个或第六个python项目),所以也许我只是做错了。

为简化起见,代码摘要如下:

tree = elementtree.ElementTree.parse(inputfile)
i = tree.getiterator()
for e in i:
    e.text = filter(e.text)
tree.write(outputfile)

阅读 167

收藏
2020-12-20

共1个答案

一尘不染

在@bobince的答案和这两个帮助下(设置属性顺序,覆盖模块方法

我设法修复了这只猴子的脏污,建议使用另一个模块更好地处理这种情况,但是在这种情况下是不可能的:

# =======================================================================
# Monkey patch ElementTree
import xml.etree.ElementTree as ET

def _serialize_xml(write, elem, encoding, qnames, namespaces):
    tag = elem.tag
    text = elem.text
    if tag is ET.Comment:
        write("<!--%s-->" % ET._encode(text, encoding))
    elif tag is ET.ProcessingInstruction:
        write("<?%s?>" % ET._encode(text, encoding))
    else:
        tag = qnames[tag]
        if tag is None:
            if text:
                write(ET._escape_cdata(text, encoding))
            for e in elem:
                _serialize_xml(write, e, encoding, qnames, None)
        else:
            write("<" + tag)
            items = elem.items()
            if items or namespaces:
                if namespaces:
                    for v, k in sorted(namespaces.items(),
                                       key=lambda x: x[1]):  # sort on prefix
                        if k:
                            k = ":" + k
                        write(" xmlns%s=\"%s\"" % (
                            k.encode(encoding),
                            ET._escape_attrib(v, encoding)
                            ))
                #for k, v in sorted(items):  # lexical order
                for k, v in items: # Monkey patch
                    if isinstance(k, ET.QName):
                        k = k.text
                    if isinstance(v, ET.QName):
                        v = qnames[v.text]
                    else:
                        v = ET._escape_attrib(v, encoding)
                    write(" %s=\"%s\"" % (qnames[k], v))
            if text or len(elem):
                write(">")
                if text:
                    write(ET._escape_cdata(text, encoding))
                for e in elem:
                    _serialize_xml(write, e, encoding, qnames, None)
                write("</" + tag + ">")
            else:
                write(" />")
    if elem.tail:
        write(ET._escape_cdata(elem.tail, encoding))

ET._serialize_xml = _serialize_xml

from collections import OrderedDict

class OrderedXMLTreeBuilder(ET.XMLTreeBuilder):
    def _start_list(self, tag, attrib_in):
        fixname = self._fixname
        tag = fixname(tag)
        attrib = OrderedDict()
        if attrib_in:
            for i in range(0, len(attrib_in), 2):
                attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1])
        return self._target.start(tag, attrib)

# =======================================================================

然后在您的代码中:

tree = ET.parse(pathToFile, OrderedXMLTreeBuilder())
2020-12-20