Listenelemente in XML in best. Reihenfolge einfügen

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
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Hallo alle miteinande,

ich bräuchte mal wieder Hilfe. :oops:
Ich habe eine XML-Datei, diese soll nun mit Hilfe von Python erweitert. Also neue Knoten mit entsprechenden Inhalt. Dabei will ich aber das bestimmte vorhandene Knoten entfernt werden.
In der Doku zu ElementTree hab ich die Methode '__delitem__' und '__delslice' gefunden, nur weiß ich grad nicht so wirklich, wie ich das einsetzen kann.

Des Weiteren hab ich mehrdimensionales Array, deren Inhalt in die XML eingetragen werden soll. Dabei soll innerhalb eines Tags 2 Elemente des Arrays eingefügt werden. das aktuelle sowie das folge Element.

Hier ist erstmal mein bisher vorhandener Programmcode.

Code: Alles auswählen

#-*- coding: utf8 -*- 
from xml.etree import ElementTree as etree

bsp_liste = ((0, 12, 26), (1, 54, 42), (2, 47, 21), (4, 12, 324), (5, 111, 0), (6, 111, 44), ...)
dateipfad = "datei.xml"
doc = open(dateipfad, 'rw')
tree = etree.parse(doc)
root = tree.getroot()
kats = tree.getroot().find("kategorien")
haupknoten_1 = tree.getroot().find("hauptknoten")

for i, a, b in bsp_liste:
        kat = etree.SubElement(kats, id="id%d" %i, name="BSP%d" %(i+1)
        punkt = etree.SubElement(kat, x="%d" %x, y="%d" %y

for i, a, b in bsp_liste:
	unterknoten = etree.SubElement(hauptknoten, "unterknoten", end_id="id%d" %(i+1), start_id="id%d" %i)
	unter_unterknoten_1 = etree.SubElement(unterknoten, "untereEbene", a="%d" %a, b="%d" %b)
	unter_unterknoten_2 = etree.SubElement(unterknoten, "untereEbene", a="%d" %a, b="%d" %b)

etree.dump(root)
doc.write(root)
doc.close()
Wie gesagt es sollen als erstes die vorhandenen Elemente innerhalb des Knotens "Kategorien" gelöscht/entfernt werden, um dann den Inhalt des Arrays einzufügen. Bis auf das Löschen funktioniert das auch.
Dabei besteht jedes Listenelement des Arrays aus einer ID, X- und Y-Koordinaten.
Im Code (oben) werden mir ja bisher im entsprechenden Tag 2 mal die Punktkoordinaten eingefügt, aber jedes mal bisher die beiden aktuellen, ich will aber das aktuelle und das folge Element.
Hat jemand eine Idee?

Die XML-Datei soll dann später in dieser Form gespeichert werden.

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beispiel SYSTEM "beispiel.dtd">
<wurzel version="1.2">
  <kategorien>
    <kategorie>
	    <punkt x="12" y="26"/>
	</kategorie>
	<kategorie>
		<punkt x="54" y="42"/>
	</kategorie>
	<kategorie>
		<punkt x="88" y="55"/>
	</kategorie>
	...
  </kategorien>
  <hauptkategorie>
    <unterknoten>
	  <untereEbene x="12" y="26"/>
	  <untereEbene x="54" y="42"/>
	</unterknoten>
	<unterknoten>
	  <untereEbene x="54" y="42"/>
	  <untereEbene x="47" y="21"/>
	</unterknoten>
	...
  </hauptkategorie>
</wurzel>
Vielen Dank schon mal

Daniela
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

In der Doku zu itertools findet sich folgendes Rezept:

Code: Alles auswählen

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)
Damit sollte das Problem mit den Pärchen gelöst sein.

Zum Löschen gibt es laut Doku bei ElementTree Elment.remove(subelement). Somit sollte das auch leicht zu machen sein. In Deinem Fall müßtest Du also entweder über die Childelemente von <kategorien> iterieren und das entsprechend aufrufen, oder vermutlich besser unterhalb von wurzel das child <kategorien> selber löschen und diesen Knoten danach neu generieren.
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Aha ok, ich versuch es mal mit dem ``remove()``, also dass ich einmal komplett alles in dem Zweig "Kategorien" lösche.

Code: Alles auswählen

kats = tree.getroot().find("kategorien")
etree.Element("kategorien").remove("kategorien")
Aber wenn ich das nach dem Schema mache, dann bekomme ich folgendes:

Code: Alles auswählen

File "*\punkte_zeichnen.py", in line 34 in <module>
  etree.Element("kategorien").remove("kategorien")
File "C:\Python\25\lib\xml\etree\ElementTree.py", line 309, in remove
  assert iselement(element)
AssertionError
Daniela
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich denke der Parameter für ``remove()`` muss ein ElementTree-Element sein und kein String.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Wenn ich in ``remove()`` das Element "kats" einfüge, welches ich vorher bestimmt habe/gefunden habe,

Code: Alles auswählen

kats = tree.getroot().find("kategorien")
etree.Element("kategorien").remove(kats) 
bekomme ich einen ValueError

Code: Alles auswählen

File "C:\Python\25\lib\xml\etree\ElementTree.py", line 309, in remove
self._children.remove(element)
ValueError: list.remove(x): x not in list
Oder muss man das vielleicht ganz anders machen?

Daniela
BlackJack

@Daniela: Du erzeugst ein Element und versuchst davon einen Kindknoten zu entfernen. Wie sollte das auch funktionieren!?
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Also in der XML-Datei die durch ein anderes Programm erstellt wird, ist der Knoten "kategorien" schon mit 3 Werten gefüllt. Das heißt der Ast "Kategorie" wurde schon 3mal geschrieben.
Diese will ich erstmal entfernen um sie dann neue Äste mit den Werten der Liste zu füllen.
Wobei eigentlich theoretisch die ersten 2 Listenwerte übernommen werden können und der 3 geändert werden soll, sowie die weiteren Werte eingefügt werden.

Oder gibt es vielleicht irgendeine Funktion, mit welcher man bestimmte Unterknoten updaten kann? Das würde ja auch schon reichen.

Aber ich glaube dass es einfacher wäre erstmal die vorhandenen Knoten (kategorie) zu löschen um dann neue einzufügen.

Und da ja schon festgestellt wurde, dass einzelne Elemente vorher noch durchiteriert werden müssten, wollte ich gleich den Ast "kategorien" löschen
Einfach ausgedrückt, sieht die XML am Anfang so aus

Code: Alles auswählen

wurzel
  a
    b
      c
    \b
    b
      c
    \b
    b
      c
    \b
  \a
  d
  \d
\wurzel
An sich sollen die einzelnen b-Tags entfernt werden, aber um es sich einfacher zu machen, soll gleich das gesamte a-Tag gelöscht werden um dann neu erstellt zu werden.

Daniela
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Daniela hat geschrieben:Wenn ich in ``remove()`` das Element "kats" einfüge, welches ich vorher bestimmt habe/gefunden habe,

Code: Alles auswählen

kats = tree.getroot().find("kategorien")
etree.Element("kategorien").remove(kats) 
Ich bin nicht ganz so sicher im Umgang mit `ElementTree`, aber ich glaube dein Code findet das, was im Knoten `kategorien` steht, erzeugt anschließend ein Element `kategorien' und versucht daraus etwas zu entfernen (was aber nicht vorhanden ist, weil du das Element ja gerade erst neu erzeugt hast). Eigentlich willst du wahrscheinlich mit dem Suchergebnis, das an `kats` gebunden ist, weiterarbeiten und davon etwas entfernen. Also tu das auch. ;)
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

ich glaub, da ich sowieso fast alles aus der alten datei lösche, kann ich die xml dann auch gleich komplett neu erstellen und nicht nur zusätzlich was einfügen. Ich denke das ist dann fast noch die einfachste Methode.

Trotzdem bleibt ja dann immernoch das 2. Problem/Fragestellung aus meinem 1. Post, nämlich wie bekomme ich das aktuelle und das Folgeelement ausgelesen/eingefügt.

Den Tipp von Hyperion hab ich mir auch schon angesehen, nur weiss ich nicht so recht, wo und wie ich es einsetzte.
Hat da jemand eine Idee/Hinweis?

Daniela
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Daniela hat geschrieben:wie bekomme ich das aktuelle und das Folgeelement ausgelesen/eingefügt.
Ich weiß nicht genau, was du damit meinst. Falls es das erste und zweite Element im Code sein soll, würde mir spontan `findall()` einfallen, wo man dann das Element mit Index 0 bzw 1 rausfischt. Vielleicht gibt es als elegantere Methode ja auch so etwas wie `next()` bzw die Möglichkeit, einen Iterator zu erstellen, von dem man ein Element weiter springen kann. Das hätte den Vorteil, dass du nicht erst unnötigerweise eine Liste mit allen Elementen erstellen müsstest, wo du genau weiß, dass dich eigentlich nur die beiden ersten Elemente interessieren. Vielleicht missverstehe ich dich aber auch und du meinst etwas ganz anderes. :D ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Daniela hat geschrieben:Hat da jemand eine Idee/Hinweis?
Mit lxml ist das ziemlich trivial, dort hat jedes Element einfach eine ``getnext()``-Methode, mit der man das nachfolgende Element auf der selben Ebene bekommt.

snafu: ``findall()`` findet alle Elemente die dem Query entsprechen, diese müssen nicht umbedingt nachfolgend sein. Zudem man mit den beschränkten Abfragemöglichkeiten von ElementTree AFAIK nicht genau festlegen dass nur die Kindelemente eines bestimmten Knotens in das Ergebnis aufgenommen werden können - ich kann mich da aber auch irren und ElementTree kann mehr als ich dachte.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

@snafu

ich will von meiner Liste bei jedem Schleifendurchlauf jeweils das aktuelle und das nächste Element aus der Liste ausgelesen haben
Vereinfacht mal an folgendem Beispiel:
Bsp: my_list = (1,2,3,4,5,6)

Code: Alles auswählen

1. Durchlauf (ausgelesen): 
     1
     2
2. Durchlauf(ausgelesen):
     2
     3
3. Durchlauf (ausgelesen):
     3
     4
4. Durchlauf (ausgelesen):
     4
     5
5. Durchlauf (ausgelesen):
     5 
     6
In meinem Fall gesteht jedes einzelne Element aus ein einem Tripel, wobei mich aber lediglich pro Tripel nur die Werte 1 und 2 interessieren.

@Leonidas
funktioniert das auch mit einem array? Ich meine ich hätte gern wie oben schon beschrieben aus meiner Liste das aktuelle und folge Element, damit dieses dann in die xml-Datei eingetragen wird.

grüße

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

Daniela hat geschrieben:funktioniert das auch mit einem array?
Nein, erstens ist das was du Array nennst eine Liste und zweitens geht das nur für lxml-ElementTrees.
Daniela hat geschrieben:Ich meine ich hätte gern wie oben schon beschrieben aus meiner Liste das aktuelle und folge Element, damit dieses dann in die xml-Datei eingetragen wird.
Nimm einfach Hyperions Beispiel, das ist doch nicht so schwer:

Code: Alles auswählen

from itertools import tee, izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

a = range(10)
print list(pairwise(a))
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Wenn ich den Code mal testweise ausführe, nur um zu sehen, ob es auch das macht, was ich will bekomme ich folgendes:

Code: Alles auswählen

Traceback (most recent call last):
  File "M:\Testscripte\meine_liste.py", line 11, in <module>
    print list(pairwise(a))
  File "M:\Testscripte\meine_liste.py", line 7, in pairwise
    next(b, None)
NameError: global name 'next' is not defined
und jetzt?

Ich hatte jetzt schon überlegt, dass ich neben der 1. for-Schleife noch eine 2. darin durchlaufen lasse, in diese dann immer vom Index her um 1 weiter ist.
Nur da bekomme ich dann einen IndexError (list index out of range)

Code: Alles auswählen

for i, a, b in bsp_liste:
	unterknoten = etree.SubElement(hauptknoten, "unterknoten", end_id="id%d" %(i+1), start_id="id%d" %i)
	unter_unterknoten_1 = etree.SubElement(unterknoten, "untereEbene", a="%d" %a, b="%d" %b)
       for k in range(len(bsp_liste)):
            unter_unterknoten_2 = etree.SubElement(unterknoten, 
            "untereEbene", a="%d" %(bsp_liste[k+1][1]), b="%d" %(bsp_liste[k+1][2]))
Zuletzt geändert von Daniela am Donnerstag 12. März 2009, 14:27, insgesamt 1-mal geändert.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Daniela hat geschrieben:Wenn ich den Code mal testweise ausführe, nur um zu sehen, ob es auch das macht, was ich will bekomme ich folgendes:
und jetzt?
probiere mal b.next() statt next(b, None).
Daniela
User
Beiträge: 73
Registriert: Donnerstag 19. Juni 2008, 07:32

Am Beispiel funktioniert es, jetzt bau ich mir das mal in mein Programm ein.
Antworten