Knoten aus XML-Dateien entfernen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

Hallo Leute,

ich habe gerade Python Neuland betreten und versuche mich
an einer XML Datei die so aufgebaut ist:

Code: Alles auswählen

<?xml version="1.0"?>
<!-- ctOPCServer configuration file -->
<OPCServer>
	<MinPollRate>60000</MinPollRate>
	<AlarmServer>
		<DatabaseFileAndPath>c:\programs\centro\cmi\proto\wsl.mdb</DatabaseFileAndPath>
	</AlarmServer>
	<WSL6>
		<EquipmentId>Q4POCA-L</EquipmentId>
	</WSL6>
	<CESAR>
		<Name>Q4POCA-1</Name>
		<Namespace>
			<Entry>
				<Device>Boot</Device>
				<Unit>Geschw</Unit>
				<Parameter Vartype="VT_R4">AbTol</Parameter>
			</Entry>
			<Entry>
				<Device>Boot</Device>
				<Unit>Geschw</Unit>
				<Parameter Vartype="VT_R4">AlTol</Parameter>
			</Entry>
			<Entry>
				<Device>Boot</Device>
				<Unit>Geschw</Unit>
				<Parameter Vartype="VT_R4">Aussen</Parameter>
			</Entry>
...
Jetzt möchte ich einige Einträge rausschmeisen z.B. den folgenden Knoten,
weil da Boot und Geschw drin ist, und die Datei neu schreiben.

Code: Alles auswählen

			<Entry>
				<Device>Boot</Device>
				<Unit>Geschw</Unit>
				<Parameter Vartype="VT_R4">AlTol</Parameter>
			</Entry>
Ich hab jetzt schon über eine Stunde in der Hilfe gesucht und mir auch das
Pyxml-Paket runtergeladen, komm aber einfach nicht klar damit.
Ihr seid meine letzte Hoffnung. Könnt Ihr mir einen Ansatz sagen, wie sowas zu bewerkstelligen ist. Ich könnte natürlich das File selber durchparsen, aber da gibt es doch in den XML-Modulen (die ich sehr unübersichtlich finde) sicher einen einfacheren Weg.

Grüsse
alpha
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Erst öffnest du die Datei.

Code: Alles auswählen

import xml.dom.minidom as xmldom

doc = xmldom.parse( "datei.xml" )
Jetzt musst du eine recursive Funktion schreiben, die überprüft, ob die Kinder deines Elements

Code: Alles auswählen

name = "entry"
parent = doc

for child in parent.childNodes:
   if child.nodeType == doc.ELEMENT_NODE and \
      child.nodeName == name:
         pass
Wenn du das Richtige gefunden hast, dann musst du die Einträge nur noch löschen und mit writexml in die Datei zurück schreiben.

Code: Alles auswählen

for child in node.childNodes:
    node.removeChild( child )
Der Code ist ungetestet, sollte aber funktionieren. Es reicht allerdings nicht, die sachen Hintereinander in eine Python Datei zu schreiben...
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

Hallo progchild,

danke für Deine schnelle Anwort. Ich hab mal weiterprobiert.
Was ich nun nicht verstehe ist, dass ich wenn ich

Code: Alles auswählen

print parent.childNodes
nur

Code: Alles auswählen

[<DOM Comment node " ctOPCServ...">, <DOM Element: OPCServer at 0xe28238>]
als Ausgabe bekomme. ich hätte mit allen Kindknoten gerechnet.

Wie komme ich jetzt den Baum weiter runter?

Gruss
alpha
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Das sind soweit ich sehe ja auch Nodes also könnte man folgendes versuchen: parent.childNodes[0].childNodes.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

Hallo nochmal,

@ Leonidas
jupp so gehts. Aber letztendlich müsste ich die Knoten die ich abfragen will mit

Code: Alles auswählen

print parent.childNodes[1].childNodes[7].childNodes[3].childNodes
ansprechen. Gibts da keinen MoveDown Befehl oder so, damit ich das ganze per rekursion erledigen kann. In der Hilfe hätte ich keinen solchen Befehl bei 13.6.2.2 Node Objects gefunden. Oder ist das die falsche Stelle?

Ich hoffe Ihr könnt mir noch einmal helfen, da ich nun schon auf dem richtigen Weg bin.

Danke
alpha
Clython
User
Beiträge: 151
Registriert: Samstag 21. August 2004, 13:58
Wohnort: Schweiz, BE-2500

Kommentar am Rande:

Du hättest auch einfach XSLT dafür gebrauchen können...
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Clython hat geschrieben:einfach XSLT
Ich würde es eher "kompliziert XSLT" schreiben und mich nach einer Python-Lösung umsehen..

So habe ich es immer noch nicht geschafft mit XSLT XML so zu bearbeiten dass XHTML rauskommt, dass von Firefox akzeptiert wird. Dabei sollte das eigentlich nicht so schwer sein :evil: Aber das nur so nebenbei.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Clython
User
Beiträge: 151
Registriert: Samstag 21. August 2004, 13:58
Wohnort: Schweiz, BE-2500

Es braucht einiges an Einarbeitungszeit, aber es ist auch ein mächtiges Tool. Der Fehler lag wohl weniger an XSLT, sondern an deinem XHTML Code, den du dafür schreiben musstest. Ich hab es bisher auch noch nie geschafft validen Code zu generieren.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Der Code war durchaus in Ordnung der generiert wurde, ich habe ihn mit Sablotron und xsltproc generieren lassen und in eine Datei gespeichert, die hat Firefox angezeigt ohne murren. Aber als ich Firefox die XML-Datei öffnen ließ, die das XSLT eingebunden hat, damit sein TransforMiiX daraus XHTML generiert, hat das nicht mehr funktioniert. Aber vielleicht geht es nur wenn die Dateien Online sind, damit Firefox einen Content-Type bekommt, mit dem er das als XHTML auffasst. Na, keine Ahnung.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

Ich denke mit dem Parsen komm ich soweit klar. Aber kann mir bitte jemand noch ein paar Zeilen posten, wie ich das ganze wieder in ein XML File bekomme?
Mit writexml komm ich leider garnicht klar

Ganz konkret:

Code: Alles auswählen

from xml.dom import minidom
doc = minidom.parse( "C:\\temp\\namespace_neu\\PoclA\\ctOPCServer.xml" )
...
wie kann ich ein verändertes "doc" wieder speichern?

Danke für eure Hilfe
alpha
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

alpha hat geschrieben:wie kann ich ein verändertes "doc" wieder speichern?
Ganz einfach:

Code: Alles auswählen

f = open( 'neu.xml', 'w' )
doc.writexml( f )
f.close()
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

AHHHHHH...
Das war zu einfach :-)
Danke vielmals... hatte vergessen das Outputfile mitzugeben... peinlich :oops:
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

Leider gibts noch ein Problem.
Ich gehe ja jetzt rekusiv durch den Baum durch. Wenn ich beim
<Entry> angekommen bin schau ich nach <Device> <Unit> und <Parameter>. Wenn mir die Inhalte nciht passen will ich <Entry> löschen. Jetzt bin ich aber schon in <Entry>... ist das ein Problem, denn laut Docu kann man ja nur einen Kindknoten löschen. Gibts da noch eine andere Löschfunktion?

Ich bekomm immer die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "F:\scripts\python\ctxml.py", line 43, in ?
    walknode(rootNode, outFile, level)
  File "F:\scripts\python\ctxml.py", line 13, in walknode
    walknode(node, outFile, level+1)
  File "F:\scripts\python\ctxml.py", line 13, in walknode
    walknode(node, outFile, level+1)
  File "F:\scripts\python\ctxml.py", line 28, in walknode
    node.removeChild(node)
  File "C:\Programme\Python24\Lib\site-packages\_xmlplus\dom\minidom.py", line 169, in removeChild
    raise xml.dom.NotFoundErr()
NotFoundErr: Node does not exist in this context
Noch meinen Code vergessen:

Code: Alles auswählen

def walknode(parent, outFile, level):
    for node in parent.childNodes:
        if node.nodeType == Node.ELEMENT_NODE:
            printLevel(outFile, level)
            outFile.write('Element: %s\n' % node.nodeName)
            if node.nodeName != "Entry":
                walknode(node, outFile, level+1)
            else: # wir sind da wo wir hin wollen
                """ Prüfen, ob wir den Knoten entfernen müssen """
                for values in delvalues:
                    found = 0
                    for value in values:
                        if value != "":
                            if node.childNodes[1].childNodes[0].nodeValue == value:
                                found = found + 1
                            if node.childNodes[3].childNodes[0].nodeValue == value:
                                found = found + 1
                            if node.childNodes[5].childNodes[0].nodeValue == value:
                                found = found + 1
                            if found == len(values):
                                """ Knoten zum Entfernen gefunden """
                                node.removeChild(node)
Hoffentlich habt ihr noch einen rettenden Tip für mich.
Danke
alpha
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

alpha hat geschrieben:

Code: Alles auswählen

node.removeChild(node)
Node kann nicht Gleichzeitig Eltern- und Kindelement sein. removeChild löscht das Kindelement eines Knotens. Im obrigen Fall ist node gleichzeitig sein Elternteil und sein Kind. Das geht ja mal nicht ;)
Pü-Ton
User
Beiträge: 67
Registriert: Donnerstag 8. Mai 2008, 07:52

Ich schrieb hier mal rein, weil ich ein ähnliches Problem habe wie alpha ursprünglich hatte.
Nur, dass ich nicht den ganzen Knoten "Entry" sondern nur das Kind "Unit" löschen will...
Eigentlich will ich den Inhalt aus Unit mit einem anderen Ihnalt einer Liste ersetzen...

Also:

Code: Alles auswählen

<?xml version="1.0" ?>
<environment>
    <Info>
        <Name>Ich bin`s</Name>
        <Version>die dritte</Version>
        <Date>gestern</Date>
    </Info>
    <Variables>
        <Tag>
            <Name>Essen</Name>
            <Value>Obst</Value>
        </Tag>
        <Tag>
            <Name>ooo</Name>
            <Value>aaa</Value>
        </Tag>
    </Variables>
</environment>
ist das urspüngliche XML-File.

In einer Liste steht jetzt

Code: Alles auswählen

Array=[Gemüse, rrr]
und nun soll also "Obst" mit "Gemüse" und "aaa" mit "rrr" ersetzt werden...

Bin schon kräftig am experimentieren, seh aber nicht so ganz den richtigen Weg:

Code: Alles auswählen

for elements in xml.getElementsByTagName('Variables'):
            for node in elements.childNodes:
                if node.nodeType == xml.ELEMENT_NODE and node.nodeName == "Tag":
                    for value in xml.getElementsByTagName("Tag"):
                        if value.nodeType == xml.ELEMENT_NODE:
                            print value
                            node.removeChild(node)
Und als Fehlermeldung kommt:

Code: Alles auswählen


<DOM Element: Tag at 0x12fe0f8>

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python25\lib\lib-tk\Tkinter.py", line 1403, in __call__
    return self.func(*args)
  File "C:\Python25\TSQTP\func.py", line 474, in save
    node.removeChild(node)
  File "C:\Python25\lib\xml\dom\minidom.py", line 164, in removeChild
    raise xml.dom.NotFoundErr()
NotFoundErr
heisst das jetzt, dass es keinen Knoten mehr innerhalb "Tag" gibt?

Gibt es eine einfache Methode mit replace.Child()? Wenn ja, hat jm ein Bsp ?

Danke schonmal
BlackJack

Warum die bemängelte Zeile *nie* funktioniert, hat ProgChild in seinem letzten Beitrag erklärt. Ein Knoten kann nicht sein eigenes Kind sein.

Ich weiss auch nicht, ob Du bei DOM und SAX so besonders viel Hilfe hier bekommen wirst. Das benutzt eigentlich keiner, der nicht gerade masochistisch veranlagt ist. ;-)

Mit `ElementTree` könnte das so aussehen:

Code: Alles auswählen

from xml.etree import ElementTree as etree

def main():
    new_contents = [u'Gemüse', 'rrr']
    
    doc = etree.parse('test.xml')
    value_nodes = doc.findall('Variables/Tag/Value')
    assert len(new_contents) == len(value_nodes)
    for value_node, new_content in zip(value_nodes, new_contents):
        value_node.text = new_content
    
    etree.dump(doc)
Pü-Ton
User
Beiträge: 67
Registriert: Donnerstag 8. Mai 2008, 07:52

Hi BlackJack,

wow, das ist ja der Hammer, klappt super. Werd es dann wohl mit Elementtree machen...

Vielen Dank :D :D :D :D
Pü-Ton
User
Beiträge: 67
Registriert: Donnerstag 8. Mai 2008, 07:52

Hi BlackJack,

ich beziehe mich nochmal auf Dein Beispiel von eben.
Wenn ich das so mache, bekomm ich ja wunderschön das gesamte XML-File in meiner Ausgabe von Scite angezeigt.
Aber das ist jetzt nur eine "Vorschau", oder?
Das bestehende, ausgelesene/geparste XML wurde damit ja nicht übrschrieben, oder?
Aber genau das will ich noch machen...

"echtes" XML-File löschen und mit dem bearbeiteten überschrieben.

Kannst Du mir da auf die Sprünge helfen?

Code: Alles auswählen

etree.dump(doc)
bewirkt doch die Anzeige, oder?

Muss überhaupt gelöscht werden, oder kann man mit elementtree auch einfach überschreiben und speichern?

Danke schonmal
BlackJack

`ElementTree`-Exemplare haben eine `write()`-Methode.
Antworten