Сохранять порядок атрибутов при модификации с помощью minidom

Есть ли способ сохранить исходный порядок атрибутов при обработке XML с минидомом?

Скажи, что у меня есть: <color red="255" green="255" blue="233" />когда я изменяю это с помощью minidom, атрибуты переставляются в алфавитном порядке: синий, зеленый и красный. Я хотел бы сохранить первоначальный порядок.

Я обрабатываю файл, просматривая элементы, возвращенные elements = doc.getElementsByTagName('color') а потом я делаю назначения, как это e.attributes["red"].value = "233",

7 ответов

Решение

Есть ли способ сохранить исходный порядок атрибутов при обработке XML с минидомом?

При значении minidom no тип данных, используемый для хранения атрибутов, представляет собой неупорядоченный словарь. pxdom может сделать это, хотя это значительно медленнее.

Чтобы сохранить порядок атрибутов, я сделал эту небольшую модификацию в минидоме:

from collections import OrderedDict

В классе Элемент:

__init__(...)
    self._attrs = OrderedDict()
    #self._attrs = {}
writexml(...)
    #a_names.sort()

Теперь это будет работать только с Python 2.7+, и я не уверен, работает ли он на самом деле => Используйте на свой страх и риск...

И обратите внимание, что вы не должны полагаться на порядок атрибутов:

Обратите внимание, что порядок спецификаций атрибутов в начальном теге или теге пустого элемента не имеет значения.

До 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(...)

Отказ от ответственности:

  1. Ты не должен полагаться на порядок атрибутов.
  2. мутирование класса NamedNodeMap не является потокобезопасным.
  3. Hotpatching это зло.

Вы, ребята, можете выставить столько оговорок, сколько захотите. Хотя переупорядочение атрибутов не имеет значения для программы, оно имеет значение для программиста / пользователя.

Для Фредрика было важно иметь порядок RGB, поскольку именно таков порядок цветов. Для меня это атрибут имени в частности.

сравнить

<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 из атрибутов впереди были необязательными?

Дело в том, что переупорядочение является более серьезной проблемой, чем то, что дает взамен возрастающий порядок. Это портит то, как программист думает или как функционал должен работать. По крайней мере, порядок должен быть настраиваемым / необязательным.

Извините за мой плохой английский. Это не мой основной язык.

Понятно, что атрибут 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("\"")

Удаление строки восстанавливает поведение, сохраняющее порядок исходного документа. Это хорошая идея, когда вы должны проверить с помощью инструментов сравнения, что в вашем коде нет ошибок.

1. Настройте свой собственный метод Element.writexml.

из 'minidom.py' скопируйте код записи Element в свой собственный файл.

переименуйте его в writexml_nosort,

удалите "a_names.sort ()" (python 2.7) или измените "a_names = sorted(attrs.keys())" на "a_names = attrs.keys()" (python 3.4)

измените метод Элемента на свой:

minidom.Element.writexml = writexml_nosort;

2. выберите ваш любимый заказ:

right_order = ['a', 'b', 'c', 'a1', 'b1']

3. отрегулируйте _attrs вашего элемента

node._attrs = OrderedDict( [(k,node._attrs[k]) для k в right_order ])

Есть ли способ сохранить исходный порядок атрибутов при обработке XML с помощью minidom?

Да. Начиная с Python 3.8, исходный порядок атрибутов сохраняется при сериализации XML-документа.

См. https://docs.python.org/3/library/xml.dom.minidom.html.

Атрибуты упорядочиваются в minidom при записи с помощью функции writexlm в классе Element. Делается это так:

a-name = sorted(attrs.keys())

Вы можете изменить это на

a-name = list(attrs.keys())

Для Idle мне пришлось изменить файл в /usr/lib/python3.6/xml/dom. Кажется, что Idle не следуетsys.pathприказ. Не забудьте сначала сделать резервную копию.

В итоге я использовал библиотеку lxml вместо minidom.

Другие вопросы по тегам