Сохранять порядок атрибутов при модификации с помощью minidom - PullRequest
11 голосов
/ 19 марта 2009

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

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

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

Ответы [ 7 ]

9 голосов
/ 01 декабря 2011

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

from collections import OrderedDict

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

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

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

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

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

8 голосов
/ 19 марта 2009

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

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

3 голосов
/ 08 декабря 2011

До Python 2.7 я использовал следующие hotpatching :

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 это зло.
3 голосов
/ 06 мая 2011

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

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

2 голосов
/ 10 марта 2014

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

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

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

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

1 голос
/ 17 апреля 2015

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 в правильном порядке])

0 голосов
/ 13 июня 2009

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

...