Wie nicht benötigte XML-Daten löschen und bereinigt schreiben?

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
Hawky
User
Beiträge: 3
Registriert: Mittwoch 14. April 2021, 17:41

Hallo zusammen,
ich bin relativ neu in Python , werde aber ab jetzt wohl öfter vorbeischauen :D
Ich darf im Praktikum ein Projekt mit XML-Dateien bearbeiten. Leider erhalte ich ein fehlerhaftes Ergebnis bei meinem Versuch...
Hier ein eigenes XML-Beispiel:

Code: Alles auswählen

<teilnehmer>

	<daten>
		<name>abc</name>
		<code>A111</code>
		<gruppe>1</gruppe>
	</daten>
	
	<daten>
		<name>efg</name>
		<code>A222</code>
		<gruppe>2</gruppe>
	</daten>
	
	<daten>
		<name>hij</name>
		<code>A333</code>
		<gruppe>3</gruppe>
	</daten>
	
</teilnehmer>
Ich habe Codes erhalten und würde gerne die Datensätze filtern. Daten die die gewünschten Codes besitzen werden behalten, der Rest wird gelöscht.
A111 und A222 sind gesucht und bleiben bestehen. A333 ist unwichtig und somit wird der komplette Datensatz gelöscht, also auch <name>,<gruppe> und anschließend werden die verbliebenen Daten in eine neue Datei geschrieben.
Ich habe es wie folgt probiert:

Code: Alles auswählen

import xml.etree.ElementTree as ET

xmlfile = "teilnehmer.xml"

tree = ET.parse(xmlfile)
root = tree.getroot()

for daten in root.findall('daten'):

    code = str(daten.find('code').text)
    if code != ("A111","A222"):

     root.remove(daten)

   
tree.write('teilnehmerneu.xml')

Die Datei teilnehmerneu.xml ist leider komplett leer nach dem Vorgang. Hier steht nur <teilnehmer> </teilnehmer>.

Das Ergebnis sollte aber sein:

Code: Alles auswählen

<teilnehmer>

	<daten>
		<name>abc</name>
		<code>A111</code>
		<gruppe>1</gruppe>
	</daten>
	
	<daten>
		<name>efg</name>
		<code>A222</code>
		<gruppe>2</gruppe>
	</daten>
	
</teilnehmer>
Ich bin mir nicht sicher, ob ich einen Fehler bei der if-Bedingung habe oder bereits bei der for-Schleife.

Vielleicht kann mir hier ja jemand weiterhelfen.
Danke!

VG
Hawky
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

`daten.find('code').text` ist schon ein String, das nochmal in einen String zu verwandeln, ist unsinnig. Kürzer ist findtext.
`code` ist dann ein String und kann niemals gleich einem Tuple sein. Was Du suchst ist der in-Operator.

Ich würde nicht direkt auf der XML-Struktur arbeiten, sondern erst die komplette Datei in eine für das Programm sinnvolle Struktur packen (Liste), dann eine neue Liste mit den gefilterten Werten erzeugen und diese dann wieder als XML serialisieren.

Also für den Anfang:

Code: Alles auswählen

from collections import namedtuple
import xml.etree.ElementTree as et

Teilnehmer = namedtuple("Teilnehmer", "name,code,gruppe")

def deserialize(filename):
    tree = et.parse(filename)
    return [
        Teilnehmer(
            daten.findtext('name'),
            daten.findtext('code'),
            daten.findtext('gruppe'),
        )
        for daten in tree.findall('daten')
    ]
Hawky
User
Beiträge: 3
Registriert: Mittwoch 14. April 2021, 17:41

Danke, mit "not in" konnte ich diese im XML filtern.

Ich versuche es mal mit dem von dir beschriebenen Code umzusetzen.
Hawky
User
Beiträge: 3
Registriert: Mittwoch 14. April 2021, 17:41

Wäre es denn möglich einen Platzhalter einzusetzen sodass nur Teile des Codes benötigt werden?

Code: Alles auswählen

code = daten.findtext('code')
   if code not in ("A11*","A22*"):
Hier die * am Ende der gesuchten Codes, sodass A11 und A22 enthalten sein müssen. Es kann also auch das Ergebnis A11AA oder A22-1 herauskommen.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sowas geht mit fnmatch, aber dann musst du den in-Operator ersetzen durch eine Schleife. Ungetestet etwa sowas

Code: Alles auswählen

if not all(fnmatch.fnmatch(code, pattern) for pattern in (“A*”, “B*”)):
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde da lieber das `re`-Modul verwenden, oder zumindest `fnmatchcase()` damit sich das unter Windows nicht anders verhält als unter Linux beispielsweise.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

In diesem Fall reicht ein ganz einfaches startswith:

Code: Alles auswählen

if not code.startswith(("A11", "A22")):
Antworten