XML Tag löschen mit iterator, aber wie ?

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
callsaender
User
Beiträge: 22
Registriert: Samstag 23. November 2013, 16:55

Sorry ich bin ein blutiger Anfänder und gerade am verzweifeln.

Problem: Ich möchte einen Tag in einer xml Datei entfernen:

Entfernt werden sollen die <year> tags

Hier die xml Datei

Code: Alles auswählen

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <date>
            <year>2008</year>
            <month>12</month>
        </date>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <date>
            <year>2009</year>
            <month>12</month>
        </date>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <date>
            <year>2010</year>
            <month>12</month>
        </date>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>
Das hatte ich mir so gedacht :

Code: Alles auswählen


import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()

// das funktioniert
for year in root.iter('year'):
     print year.tag

// aber das nicht 
for date in root.iter('data'):
      root.remove(year.tag)

tree.write('out.xml')

dann bekomme ich folgende Fehlermeldung :-(

Code: Alles auswählen

Traceback (most recent call last):
  File "<pyshell#31>", line 2, in <module>
    root.remove(year.tag)
  File ".\Python27\lib\xml\etree\ElementTree.py", line 337, in remove
    self._children.remove(element)
ValueError: list.remove(x): x not in list

was habe ich falsch gemacht ?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@callsaender: Du kannst mir remove nur Elemente entfernen, die auch in der Liste der Kinder ist.
Dass »year« nur zufällig in der for-Schleife davor definiert wird macht es noch schlimmer.
Mit »tree.findall('.//year/..') bekommst Du eine Liste aller Elemente die ein year-Element enthalten.
Aus diesen kannst Du mit remove die year-Unterelemente löschen.
callsaender
User
Beiträge: 22
Registriert: Samstag 23. November 2013, 16:55

weder das

Code: Alles auswählen


for listOfYearTags in root.findall('.//year/'):
	     root.remove(year)

oder das

Code: Alles auswählen

for listOfYearTags in root.findall('.//year/'):
	     root.remove(listOfYearTags)

noch das geht *grübel*

Code: Alles auswählen


for year in root.findall('year'):
     root.remove(year)

Die Syntax bringt mich noch um *grrr*
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@callsaender: raten hilft selten beim Programmieren weiter. »root« hat wohl kaum ein »year«-Element und kann daher auch nicht »remove«t werden. Was hast Du von meiner vorherigen Antwort versucht umzusetzen?

Beim ersten Code ist »year« nicht definiert. Bei den ersten beiden Versuchen ist »listOfYearTags« kein sinnvoller Name, da es sich weder um eine Liste handelt noch um Tags und weiter ist der Name nicht PEP-8-konform.
Dritter Versuch wäre richtig, wenn »root« year-Elemente enthalten würde.
callsaender
User
Beiträge: 22
Registriert: Samstag 23. November 2013, 16:55

Also das Löschen aller <bar> Tags funtioniert jetzt hier

Code: Alles auswählen

<?xml version="1.0"?>
<myDoc>
  <foo>
    <bar> de </bar> // remove this one
  </foo>
  <foo>
    <bar> de </bar> // remove this one
  </foo>
</myDoc>

Code: Alles auswählen

import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()

tree.parse('d:/_test/in.xml')
tree.write('d:/_test/out.xml')

foos = tree.findall('foo')
for foo in foos:
  bars = foo.findall('bar')
  for bar in bars:
    foo.remove(bar)

tree.write('d:/_test/out.xml')
Aber es geht nur wenn ich alle Tag-Namen kenne.
Und hier mein Problem: Gelöscht werden sollen
alle <bar> tags aber ich kenne nicht den Pfad
und dessen Tiefe. Hier als z.B. <unknow>
gekennzeichnet.

Code: Alles auswählen

<?xml version="1.0"?>
<myDoc>
  <foo>
    <bar> de </bar> // it remove this one
  </foo>
  <foo>
    <unknown>
      <bar> de </bar> // how can i remove this one
    </unknow>
    <bar> de </bar> // it remove this one
  </foo>
</myDoc>
Kann mir jemand helfen ?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Mit XPath sollte es gehen, ungetestet:

Code: Alles auswählen

for bar in tree.findall('.//bar'):
    # was auch immer
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

jerch hat geschrieben:Gelöscht werden sollen
alle <bar> tags aber ich kenne nicht den Pfad
und dessen Tiefe.
Siehe mein erstes Posting.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Wer lesen kann, ist klar im Vorteil :oops:
callsaender
User
Beiträge: 22
Registriert: Samstag 23. November 2013, 16:55

Einfacher als ich dachte :-)

Aber das dass '/..' für Parent steht
und das 'find' auf ein Child bezieht
ist eine gewöhnungsbedürftige
Syntax für mich.

Und dann die für neue Enwicklungsumgebung
von Phyton mit einem nicht gerade
komfortablen Debuger. :-/

Code: Alles auswählen

# -*- coding: cp1252 -*-
import xml.etree.ElementTree as ET
import math as m

tree = ET.parse('country_data.xml')
root = tree.getroot()

tree.parse('d:/_test/in.xml')
tree.write('d:/_test/out.xml')

for elements in tree.findall('.//bar/..'):
      print elements.find('bar')
      elements.remove(elements.find('bar'))
          
tree.write('d:/_test/out.xml')

BlackJack

@callsaender: Das '..' für „eine Ebene höher” in XPath verwendet wird, sollte man eigentlich schon von Dateipfaden kennen, wo zumindest in DOS, Windows, und Linux diese beiden Punkte mit der gleichen Bedeutung verwendet werden.

Und das sich `find()` auf das Objekt bezieht auf dem es aufgerufen wird, sollte eigentlich auch nicht verwundern.

Der Name `elements` ist etwas verwirrend. Klar hängen da noch andere Elemente an dem Element-Objekt dran, aber der Name selbst bezieht sich ja nur auf *ein* Element. Da man den Typ des Elements nicht kennt könnte man es zum Beispiel `bar_parent` nennen.

Dein Code würde übrigens nur das erste <bar>-Element entfernen. Das ist okay wenn es immer nur eines geben kann, aber falls es mehere gibt die entfernt werden sollen, müsste man an der Stelle eine Schleife über diese Elemente schreiben.
callsaender
User
Beiträge: 22
Registriert: Samstag 23. November 2013, 16:55

So, das ist erst einmal die Lösung für ein Child zu entfernen.
Bei der der Lösung mit mehren Children werde ich es mit einer
zusätzlichen while Schleife versuchen.

@BlackJack: Danke! Das war ein sehr wertvoller Tipp nach mehren
Childeren zu suchen. Die Xml Datei für die ich das werden werde
hatte versehentlich mehrer doppelte, was sie aber nicht sollte.
Das war bis Dato noch keinem aufgefallen :-)

Code: Alles auswählen

# -*- coding: cp1252 -*-
import xml.etree.ElementTree as ET
 
tree = ET.parse('in.xml')
root = tree.getroot()
 
tree.parse('d:/_test/in.xml')
tree.write('d:/_test/out.xml')
 
# Entfernt jeweils das erste 'bar' Child 
# in den Bar-Parents der in.xml Datei

for bar_parent in tree.findall('.//bar/..'):
      print bar_parent.find('bar')
      bar_parent.remove(bar_parent.find('bar'))
         
tree.write('d:/_test/out.xml')
Antworten