Seite 1 von 1

Knoten aus XML-Dateien entfernen

Verfasst: Montag 8. August 2005, 10:23
von alpha
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

Verfasst: Montag 8. August 2005, 11:37
von ProgChild
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...

Verfasst: Montag 8. August 2005, 13:58
von alpha
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

Verfasst: Montag 8. August 2005, 14:55
von Leonidas
Das sind soweit ich sehe ja auch Nodes also könnte man folgendes versuchen: parent.childNodes[0].childNodes.

Verfasst: Montag 8. August 2005, 15:43
von alpha
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

Verfasst: Montag 8. August 2005, 21:50
von Clython
Kommentar am Rande:

Du hättest auch einfach XSLT dafür gebrauchen können...

Verfasst: Montag 8. August 2005, 23:59
von Leonidas
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.

Verfasst: Dienstag 9. August 2005, 09:04
von Clython
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.

Verfasst: Dienstag 9. August 2005, 11:41
von Leonidas
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.

Verfasst: Mittwoch 10. August 2005, 09:21
von alpha
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

Verfasst: Mittwoch 10. August 2005, 11:22
von ProgChild
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()

Verfasst: Mittwoch 10. August 2005, 15:38
von alpha
AHHHHHH...
Das war zu einfach :-)
Danke vielmals... hatte vergessen das Outputfile mitzugeben... peinlich :oops:

Verfasst: Freitag 12. August 2005, 10:02
von alpha
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

Verfasst: Freitag 12. August 2005, 10:36
von ProgChild
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 ;)

Verfasst: Donnerstag 10. Juli 2008, 12:02
von Pü-Ton
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

Verfasst: Donnerstag 10. Juli 2008, 12:27
von 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)

Verfasst: Donnerstag 10. Juli 2008, 15:37
von Pü-Ton
Hi BlackJack,

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

Vielen Dank :D :D :D :D

Verfasst: Donnerstag 17. Juli 2008, 14:24
von Pü-Ton
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

Verfasst: Donnerstag 17. Juli 2008, 14:30
von BlackJack
`ElementTree`-Exemplare haben eine `write()`-Methode.