在使用 minidom 处理 XML 时,有没有办法可以保留属性的原始顺序?
假设我有:<color red="255" green="255" blue="233" />
当我用 minidom 修改它时,属性按字母顺序重新排列为蓝色、绿色和红色。我想保留原来的顺序。
我通过遍历返回的元素来处理文件,elements = doc.getElementsByTagName('color')
然后我做这样的分配e.attributes["red"].value = "233"
。
为了保持属性顺序,我做了这个微小的修改:
from collections import OrderedDict
在元素类中:
__init__(...)
self._attrs = OrderedDict()
#self._attrs = {}
writexml(...)
#a_names.sort()
现在这只适用于 Python 2.7+ 而且我不确定它是否真的有效 => 使用风险自负......
请注意,您不应依赖属性顺序:
请注意,开始标签或空元素标签中属性规范的顺序并不重要。
在使用 minidom 处理 XML 时,有没有办法可以保留属性的原始顺序?
使用 minidom no,用于存储属性的数据类型是无序字典。pxdom可以做到,尽管速度要慢得多。
很明显,xml 属性没有排序。我刚刚发现了这种奇怪的行为!
似乎这与 xml.dom.minidom.Element.writexml 函数中添加的排序有关!
class Element(Node):
... snip ...
def writexml(self, writer, indent="", addindent="", newl=""):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
writer.write(indent+"<" + self.tagName)
attrs = self._get_attributes()
a_names = attrs.keys()
a_names.sort()
--------^^^^^^^^^^^^^^
for a_name in a_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value)
writer.write("\"")
删除该行将恢复保持原始文档顺序的行为。当您必须使用差异工具检查代码中没有错误时,这是一个好主意。
在 Python 2.7 之前,我使用了以下热补丁:
class _MinidomHooker(object):
def __enter__(self):
minidom.NamedNodeMap.keys_orig = minidom.NamedNodeMap.keys
minidom.NamedNodeMap.keys = self._NamedNodeMap_keys_hook
return self
def __exit__(self, *args):
minidom.NamedNodeMap.keys = minidom.NamedNodeMap.keys_orig
del minidom.NamedNodeMap.keys_orig
@staticmethod
def _NamedNodeMap_keys_hook(node_map):
class OrderPreservingList(list):
def sort(self):
pass
return OrderPreservingList(node_map.keys_orig())
这样使用:
with _MinidomHooker():
document.writexml(...)
免责声明:
你们可以提出尽可能多的免责声明。虽然重新排序属性对程序没有意义,但它对程序员/用户确实有意义。
对于 Fredrick 来说,拥有 RGB 顺序很重要,因为这就是颜色的顺序。对我来说,特别是 name 属性。
比较
<field name="url" type="string" indexed="true" stored="true" required="true" multiValued="false"/> <!-- ID -->
<field name="forkortelse" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="kortform" type="text_general" indexed="true" stored="true" required="false" multiValued="false" />
<field name="dato" type="date" indexed="true" stored="true" required="false" multiValued="false" />
<field name="nummer" type="int" indexed="true" stored="true" required="false" multiValued="false" />
<field name="kilde" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="tittel" type="text_general" indexed="true" stored="true" multiValued="true"/>
反对
<field indexed="true" multiValued="false" name="forkortelse" required="false" stored="true" type="string"/>
<field indexed="true" multiValued="false" name="kortform" required="false" stored="true" type="text_general"/>
<field indexed="true" multiValued="false" name="dato" required="false" stored="true" type="date"/>
<field indexed="true" multiValued="false" name="nummer" required="false" stored="true" type="int"/>
<field indexed="true" multiValued="false" name="kilde" required="false" stored="true" type="string"/>
<field an_optional_attr="OMG!" an_optional_attr2="OMG!!" indexed="true" name="tittel" stored="true" type="text_general"/>
虽然阅读并非不可能,但它并不容易。名称是重要的属性。隐藏名称字段是不好的。如果名称是左边的 15 个属性,而前面的 7 个属性是可选的呢?
关键是重新排序是一个比递增排序所带来的问题更大的问题。它与程序员的思维方式或功能应该如何工作相混淆。至少排序应该是可配置的/可选的。
原谅我糟糕的英语。它不是我的主要语言。
1.自定义你自己的'Element.writexml'方法。
从 'minidom.py' 将 Element 的 writexml 代码复制到您自己的文件中。
将其重命名为 writexml_nosort,
删除 'a_names.sort()' (python 2.7) 或将 'a_names = sorted(attrs.keys())' 更改为 'a_names = attrs.keys()' (python 3.4)
将 Element 的方法更改为您自己的方法:
minidom.Element.writexml = writexml_nosort;
2.自定义您喜欢的订单:
right_order = ['a', 'b', 'c', 'a1', 'b1']
3.调整你元素的 _attrs
node._attrs = OrderedDict( [(k,node._attrs[k]) for k in right_order ])
使用类 Element 中的 writexlm 函数编写时,属性按最小顺序排列。它是这样完成的:
a-name = sorted(attrs.keys())
您可以将其更改为
a-name = list(attrs.keys())
对于空闲,我不得不将文件更改为
/usr/lib/python3.6/xml/dom
. 似乎 Idle 不遵循sys.path
顺序。不要忘记先备份。
在使用 minidom 处理 XML 时,有没有办法可以保留属性的原始顺序?
是的。从 Python 3.8 开始,序列化 XML 文档时会保留原始属性顺序。
请参阅https://docs.python.org/3/library/xml.dom.minidom.html#xml.dom.minidom.Node.writexml。
我最终使用了lxml库而不是 minidom。