Frage

Ich habe eine ziemlich einfache Filter in Python geschrieben ElementTree mit den Kontexten von einigen XML-Dateien munge. Und es funktioniert, mehr oder weniger.

Aber es ordnet die Attribute von verschiedenen Tags, und ich möchte, dass nicht tun.

Hat jemand einen Schalter weiß, dass ich werfen kann, um es ihnen zu machen halten in der angegebenen Reihenfolge?

Kontext für dieses

Ich arbeite mit und auf einem Teilchenphysik-Tool, das ein komplexes, aber seltsam Konfigurationssystem beschränkt, basierend auf XML-Dateien. Unter den vielen Sachen Setup sind auf diese Weise die Pfade zu verschiedenen statischen Datendateien. Diese Pfade werden in die bestehenden XML fest einprogrammiert, und es gibt keine Einrichtungen zum Einstellen oder Verändern sie anhand von Umgebungsvariablen und in unserer lokalen Installation sind sie unbedingt an einem anderen Ort.

Dies ist keine Katastrophe, da die kombinierte Quellen- und Build-Steuerungstool verwenden wir uns bestimmte Dateien mit lokalen Kopien Schatten ermöglicht. Aber dachte auch die Datenfelder sind statisch die xml nicht der Fall, so dass ich ein Skript geschrieben haben, für die Pfade Festsetzung, aber mit dem Attribut Umlagerung diffs zwischen den lokalen und Master-Versionen sind härter als nötig zu lesen.


Dies ist mein erstes Mal ElementTree für eine Spritztour nehmen (und nur meinem fünften oder sechsten Python-Projekt) so vielleicht falsch, ich mache es einfach.

Abstracted der Einfachheit halber Der Code sieht wie folgt aus:

tree = elementtree.ElementTree.parse(inputfile)
i = tree.getiterator()
for e in i:
    e.text = filter(e.text)
tree.write(outputfile)

Vernünftige oder stumm?


Links zum Thema:

War es hilfreich?

Lösung

Mit Hilfe Antwort der @ bobince und dieses beide ( Einstellung Attribut um , übergeordnete Modul Methoden )

konnte ich diesen Affen bekommen es schmutzigen gepatcht und ich würde ein anderes Modul vorschlagen, mit, dass eine bessere Griffe dieses Szenario aber wenn das nicht eine Möglichkeit:

# =======================================================================
# 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)

# =======================================================================

Dann im Code:

tree = ET.parse(pathToFile, OrderedXMLTreeBuilder())

Andere Tipps

Nein. ElementTree verwendet ein Wörterbuch zu speichern Attributwerte, so ist es von Natur aus ungeordnet.

Auch DOM nicht garantieren Sie Ordnungsattribut und DOM macht viel mehr Detail des XML-Infoset als ElementTree tut. (Es gibt einige Dome, die es als Feature bieten, aber es ist nicht Standard.)

Kann es behoben werden? Vielleicht. Hier ist ein Stich an, dass das Wörterbuch ersetzt, wenn sie mit einem Parsen bestellt ein ( 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')])

Sieht aus potenziell vielversprechend.

>>> s = StringIO.StringIO()
>>> tree.write(s)
>>> s.getvalue()
'<a b="c" d="e" f="g" h="i" j="k" />'

Bah, die Serializer gibt sie in kanonischer Reihenfolge.

Das sieht aus wie die Linie Schuld, in ElementTree._write:

            items.sort() # lexical order

Subclassing oder Affen-Patching, das ärgerlich sein wird, wie es ist direkt in der Mitte einer großen Methode.

Es sei denn, du hast etwas Gemeines wie Unterklasse OrderedDict und Hack items eine spezielle Unterklasse von list zurückzukehren, dass ignoriert Anrufe sort(). Nein, wahrscheinlich, dass noch schlimmer und ich ins Bett gehen sollte, bevor ich komme mit etwas schrecklicher als dem.

Falsche Frage. Sollte sein: „Wo finde ich eine diff Gadget finden, die sinnvollerweise mit XML-Dateien funktioniert

Antwort: Google ist dein Freund. Erstes Ergebnis für die Suche auf "xml diff" => dieser . Es gibt noch ein paar possibles.

Ja, mit 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"/>'

Hier ist direkt Link Dokumentation von welche das obige Beispiel leicht angepasst ist.

Beachten Sie auch, dass lxml hat, durch Design, einige gute API Kompatibilität mit Standard xml.etree.ElementTree

beste Option ist, die lxml Bibliothek http://lxml.de/ verwenden Installieren des lxml und Schalt nur die Bibliothek hat die Magie zu mir.

#import xml.etree.ElementTree as ET
from lxml import etree as ET

Aus Abschnitt 3.1 der der XML-Empfehlung :

Beachten Sie, dass die Reihenfolge der Attributspezifikationen in einem Start-Tag oder Leeres-Element-Tag ist nicht signifikant.

Jedes System, das auf der Reihenfolge der Attribute in einem XML-Elemente setzt wird brechen.

hatte ihr Problem. Zum einen sah für einige Python-Skript canonize, fand nur knapp sein Ziel jedermann. Dann begann man darüber nachzudenken, zu machen. Schließlich xmllint gelöst.

Dies ist eine Teillösung, für den Fall, xml emittiert wird, und eine vorhersagbare Reihenfolge gewünscht wird. Dabei spielt es keine Rundreise Parsen und Schreiben lösen. Sowohl 2.7 und 3.x Verwendung sorted() zu zwingen, ein Attribut Bestellung. Also, dieser Code in Verbindung mit der Verwendung eines OrderedDictionary die Attribute halten wird die Reihenfolge für XML-Ausgabe zu erhalten, um die Bestellung entspricht verwendet, um die Elemente zu erstellen.

from collections import OrderedDict
from xml.etree import ElementTree as ET

# Make sorted() a no-op for the ElementTree module
ET.sorted = lambda x: x

try:
    # python3 use a cPython implementation by default, prevent that
    ET.Element = ET._Element_Py
    # similarly, override SubElement method if desired
    def SubElement(parent, tag, attrib=OrderedDict(), **extra):
        attrib = attrib.copy()
        attrib.update(extra)
        element = parent.makeelement(tag, attrib)
        parent.append(element)
        return element
    ET.SubElement = SubElement
except AttributeError:
    pass  # nothing else for python2, ElementTree is pure python

# Make an element with a particular "meaningful" ordering
t = ET.ElementTree(ET.Element('component',
                       OrderedDict([('grp','foo'),('name','bar'),
                                    ('class','exec'),('arch','x86')])))
# Add a child element
ET.SubElement(t.getroot(),'depend',
              OrderedDict([('grp','foo'),('name','util1'),('class','lib')]))  
x = ET.tostring(n)
print (x)
# Order maintained...
# <component grp="foo" name="bar" class="exec" arch="x86"><depend grp="foo" name="util1" class="lib" /></component>

# Parse again, won't be ordered because Elements are created
#   without ordered dict
print ET.tostring(ET.fromstring(x))
# <component arch="x86" name="bar" grp="foo" class="exec"><depend name="util1" grp="foo" class="lib" /></component>

Das Problem mit XML in eine Elementstruktur Parsen ist, dass der Code intern Ebene dicts erzeugt, die in dem Elemente geführt werden (), an welchem ??Punkt der Ordnung verloren geht. Keine Entsprechung einfache Patch ist möglich.

habe ich die akzeptierte Antwort oben, mit beiden Aussagen:

ET._serialize_xml = _serialize_xml
ET._serialize['xml'] = _serialize_xml

Während dies die Bestellung in jedem Knoten festgelegt, Attribut Bestellung auf neue Knoten von Kopien des vorhandenen Knoten eingefügt ausgefallen ohne deep zu bewahren. Achten Sie auf Knoten Wiederverwendung andere zu schaffen ... In meinem Fall hatte ich ein Element mit mehreren Attributen, so dass ich sie wiederverwenden wollte:

to_add = ET.fromstring(ET.tostring(contract))
to_add.attrib['symbol'] = add
to_add.attrib['uniqueId'] = add
contracts.insert(j + 1, to_add)

Die fromstring(tostring) werden die Attribute im Speicher neu anordnen. Es kann nicht dazu führen, in der Alpha-dict von Attributen sortiert, aber es kann auch nicht die erwartete Ordnung haben.

to_add = copy.deepcopy(contract)
to_add.attrib['symbol'] = add
to_add.attrib['uniqueId'] = add
contracts.insert(j + 1, to_add)

Nun ist die Bestellung weiter besteht.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top