2009-03-19 17 views
10

Czy istnieje sposób zachowania pierwotnej kolejności atrybutów podczas przetwarzania XML za pomocą minidom?Zachowaj kolejność atrybutów podczas modyfikowania za pomocą minidom

Powiedz, że mam: <color red="255" green="255" blue="233" /> kiedy modyfikuję to za pomocą minidomu, atrybuty są uporządkowane alfabetycznie na niebiesko, zielono i czerwono. Chciałbym zachować oryginalne zamówienie.

Przetwarzam plik przez zapętlenie elementów zwróconych przez elements = doc.getElementsByTagName('color'), a następnie robię zadania takie jak to e.attributes["red"].value = "233".

Odpowiedz

8

Czy istnieje sposób zachowania pierwotnej kolejności atrybutów podczas przetwarzania XML za pomocą minidom?

Przy użyciu minidomu nie, typem danych używanym do przechowywania atrybutów jest nieuporządkowany słownik. pxdom może to zrobić, chociaż jest znacznie wolniejsze.

-1

Skończyło się na używaniu biblioteki lxml zamiast minidom.

+1

na przykład, patrz [ten post] (http: // StackOverflow. com/a/34560411/540510) – thdox

3

Jest oczywiste, że atrybut xml nie jest uporządkowany. Po prostu znalazłem to dziwne zachowanie!

Wygląda na to, że dotyczy to sortowania dodanego w funkcji 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("\"") 

Usunięcie linii przywraca zachowanie zachowujące kolejność oryginalnego dokumentu. Jest to dobry pomysł, gdy trzeba sprawdzić za pomocą narzędzi diff, że nie ma błędu w kodzie.

8

Aby zachować kolejność atrybutów Zrobiłem to nieznaczne zmiany w minidom:

from collections import OrderedDict 

W klasie element:

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

Teraz to będzie działać tylko z Python 2.7+ I jestem nie wiem, czy to faktycznie działa => Używaj na własne ryzyko ...

Pamiętaj, że nie powinieneś polegać na kolejności atrybutów:

Należy zauważyć, że kolejność specyfikacji atrybutów w znaczniku start-tag lub empty-element nie jest znacząca.

+0

Jak zmodyfikowałeś t on Element klasy? – NPike

+0

Nadal działa na Pythonie 3.2, zamień 'a_names = sorted (attrs.keys())' przez 'a_names = attrs.keys()' –

3

Przed Pythonie 2.7, użyłem następujących funkcji 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()) 

Używany w ten sposób:

with _MinidomHooker(): 
    document.writexml(...) 

Zastrzeżenie:

  1. ty nie powinna polegać na zlecenie atrybuty.
  2. Mutowanie klasy NamedNodeMap nie jest bezpieczne dla wątków.
  3. hotpatching jest zły.
2

Możesz wymyślić jak najwięcej żądań. Podczas zmiany kolejności atrybutów nie ma znaczenia dla programu, ma to znaczenie dla programisty/użytkownika.

Dla Fredrick ważne było posiadanie zlecenia RGB, ponieważ jest to kolejność kolorów. Dla mnie jest to w szczególności atrybut nazwy.

Porównaj

<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"/> 

Przeciwko

<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"/> 

Chociaż nie jest to niemożliwe, aby czytać to nie jest tak łatwo. Nazwa jest ważnym atrybutem. Ukrywanie pola nazwy nie jest dobre. Co się stanie, jeśli nazwa ma 15 atrybutów po lewej stronie, gdzie 7 atrybutów z przodu było opcjonalne?

Chodzi o to, że zmiana kolejności jest większym problemem niż to, co w zamian daje zlecenie acsending. To zakłóca sposób, w jaki programista myśli, lub w jaki sposób funkcjonalność ma działać. Przynajmniej zamawianie powinno być konfigurowalne/opcjonalne.

Przepraszam za mój słaby angielski. To nie jest mój główny język.

+3

To, co tu mówisz, nie jest nieuzasadnione. Ale to nie jest odpowiedź na pytanie. – mzjn

+0

Nie rozumiem –

+0

Całkowicie się zgadzam z tym, co mówisz, ale to naprawdę powinien być komentarz, mimo że jest za duży. –

1

1.Zapasuj własną metodę "Element.writexml".

z "minidom.py" skopiuj kod writexml elementu do własnego pliku.

nazwy go writexml_nosort,

usuwania 'a_names.sort()' (pyton 2,7) lub zmiana 'a_names = attrs.keys sortowanie (())' do 'a_names = attrs.keys()' (Python 3.4)

zmiana metoda elementu do własnych:

minidom.Element.writexml = writexml_nosort;

2.custom swoją ulubiony zamówienie:

right_order = [ 'a', 'b', 'c', 'A1', 'B1']

3.adjust elemencie jest _attrs

node._attrs = OrderedDict ([(k node._attrs [k]), k]) w right_order

Powiązane problemy