dmc*_*kee 22 python xml elementtree
我在python中编写了一个相当简单的过滤器,使用ElementTree来处理某些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)
Run Code Online (Sandbox Code Playgroud)
合理还是愚蠢?
相关链接:
Sne*_*oda 22
在@ 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)
# =======================================================================
Run Code Online (Sandbox Code Playgroud)
然后在你的代码中:
tree = ET.parse(pathToFile, OrderedXMLTreeBuilder())
Run Code Online (Sandbox Code Playgroud)
bob*_*nce 19
不.ElementTree使用字典来存储属性值,因此它本身就是无序的.
即使DOM也不保证您的属性排序,并且DOM公开了XML信息集的更多细节,而不是ElementTree.(有些DOM确实提供了它作为一项功能,但它并不标准.)
可以修复吗?也许.这是对它的一种刺激,它在用有序的一个(collections.OrderedDict()
)进行解析时替换了字典.
from xml.etree import ElementTree
from collections import OrderedDict
import StringIO
class OrderedXMLTreeBuilder(ElementTree.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)
>>> xmlf = StringIO.StringIO('<a b="c" d="e" f="g" j="k" h="i"/>')
>>> tree = ElementTree.ElementTree()
>>> root = tree.parse(xmlf, OrderedXMLTreeBuilder())
>>> root.attrib
OrderedDict([('b', 'c'), ('d', 'e'), ('f', 'g'), ('j', 'k'), ('h', 'i')])
Run Code Online (Sandbox Code Playgroud)
看起来很有希望.
>>> s = StringIO.StringIO()
>>> tree.write(s)
>>> s.getvalue()
'<a b="c" d="e" f="g" h="i" j="k" />'
Run Code Online (Sandbox Code Playgroud)
Bah,序列化器以规范顺序输出它们.
这看起来像是责备,在ElementTree._write
:
items.sort() # lexical order
Run Code Online (Sandbox Code Playgroud)
子类化或猴子修补会让人讨厌,因为它正处于一个大方法的中间.
除非你做了像子类这样令人讨厌的东西OrderedDict
而且黑客items
返回一个list
忽略调用的特殊子类sort()
.不,可能那更糟,我应该睡觉才能想出比这更可怕的东西.
最好的选择是使用lxml库http://lxml.de/ 安装lxml并只需切换该库就对我产生了魔力。
#import xml.etree.ElementTree as ET
from lxml import etree as ET
Run Code Online (Sandbox Code Playgroud)
是的,使用lxml
>>> from lxml import etree
>>> root = etree.Element("root", interesting="totally")
>>> etree.tostring(root)
b'<root interesting="totally"/>'
>>> print(root.get("hello"))
None
>>> root.set("hello", "Huhu")
>>> print(root.get("hello"))
Huhu
>>> etree.tostring(root)
b'<root interesting="totally" hello="Huhu"/>'
Run Code Online (Sandbox Code Playgroud)
这里是文档的直接链接,上面的示例略有改动。
还要注意,根据设计,lxml与标准xml.etree.ElementTree具有一些良好的API兼容性。
这已在 python 3.8 中“修复”。我在任何地方都找不到关于它的任何注释,但它现在可以工作了。
D:\tmp\etree_order>type etree_order.py
import xml.etree.ElementTree as ET
a = ET.Element('a', {"aaa": "1", "ccc": "3", "bbb": "2"})
print(ET.tostring(a))
D:\tmp\etree_order>C:\Python37-64\python.exe etree_order.py
b'<a aaa="1" bbb="2" ccc="3" />'
D:\tmp\etree_order>c:\Python38-64\python.exe etree_order.py
b'<a aaa="1" ccc="3" bbb="2" />'
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
22050 次 |
最近记录: |