XML-Config-Datei mit BS auch modifizieren?

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.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

print("Hello world!")... tja, kann das jemand überbieten? :lol:

So, jetzt genug gealbert und zurück zu den ganz wichtigen Dingen:
-> etree !!

Ich habe bisher etree nur zum erstellen von XML verwendet. Habe mit BS HTML geparsed und definierte Elemente in eine XML gejagt (siehe irgend ein anderen Noob-Thread von mir irgendwo hier).

So, wie finde ich etwas ganz speizielles mit etree:
Ich habe als Beispiel
[Codebox=xml file=Unbenannt.xml]
<stadtliste>
<stadt>
<name>München</name>
<jahr aktiv="1">2015</jahr>
</stadt>
<stadt>
<name>Stuttgart</name>
<jahr aktiv="0">2015</jahr>
</stadt>
<stadt>
<name>Frankfurt</name>
<jahr aktiv="0">2014</jahr>
<jahr aktiv="1">2015</jahr>
</stadt>
<stadt>
<name>Köln</name>
<jahr aktiv="1">2014</jahr>
<jahr aktiv="1">2015</jahr>
</stadt>
</stadtliste>
[/Codebox]

Jetzt will ich alle Parent-Nodes selektieren die im Jahr-Tag das Atribut aktiv=1 haben.

Später dann (nur zur Vorwarnung denn nach Eins kommt erst Zwei)
will ich die selektierten Nodes nochmals durchsuchen um alle aktiv=1 zu sammeln...

Ich habe es schon probiert mit:

Code: Alles auswählen

for stadt in root.findall("..//jahr[@aktiv='1']"):
	print(stadt.find('name').text)
aber python bringt nichts raus. Ohne das ich jetzt ein Atribut eines Subelements suche bekomme ich schon Ergebnisse, also die Richtung stimmt schon irgendwie...
BlackJack

@kaineanung: Schau Dir mal an was `stadt` gebunden ist, da kann es kein 'name' geben.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Du willst mir wohl sagen das die Stadt an das Jahr gebunden ist, richtig?
Aber ich dachte mit dem "..//jahr[@aktiv='1']" im Findall des etrees im root würde ich folgende Dinge machen:
Vom gefundenen Element weches <jahr aktiv="1"> beinhaltet, dessen Parent (..) und dann alle gleichen SubElemente (//) selektieren.
So oder so ähnlich jedenfalls in der Beschreibung von XPath-Syntax Support im etree....

Aber wenn ich es falsch mache, wie sol ich es dann machen falls es überhaupt geht? Oder muss ich alle 'Stadt'-Elemente ohne Einschränkung selektieren und dann in der Schleife die SubElemente untersuchen um zu entscheiden was ich nehme und was nicht?
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@kaineanung: ich würde ja gar nicht auf den XML-Daten arbeiten, sondern sie in eine passende Datenstruktur überführen, z.B. so:

Code: Alles auswählen

staedte = [
    {'name': s.findtext('name'),
      'jahre': [(int(j.text), bool(int(j.get('aktiv', 0)))) for j in s.findall('jahr')]
    } for s in root.findall('stadt')
]
BlackJack

@kaineanung: XPath wird von links nach rechts ausgewertet. Das '..' wird demnach als erstes ausgewertet und führt Dich zum Element über `root`. Ich wüsste jetzt aus dem Kopf nicht mal was das in diesem Fall konkret bedeutet. Normalerweise hat man an der Stelle nur einen '.' zu stehen. Mit '//' werden dann alle Subelemente angesprochen und mit 'jahr' alle <jahr>-Elemente. Mit '[@aktiv="1"]' werden die <jahr>-Elemente gefiltert bei denen das Attribut `aktiv` den Wert '1' hat. Und das ist dann Dein Ergebnis. Wenn Du von diesen <jahr>-Elementen die jeweiligen Eltern-Elemente haben willst musst Du das danach als '/..' angeben.

Bei solchen Sachen finde ich die Python-Shell immer sehr praktisch weil man da live ausprobieren und überprüfen kann wie die Ergebnisse bei verschiedenen XPath-Ausdrücken aussehen:

Code: Alles auswählen

In [21]: source = '''
   ....: <stadtliste>
   ....: <stadt>
   ....: <name>München</name>
   ....: <jahr aktiv="1">2015</jahr>
   ....: </stadt>
   ....: <stadt>
   ....: <name>Stuttgart</name>
   ....: <jahr aktiv="0">2015</jahr>
   ....: </stadt>
   ....: <stadt>
   ....: <name>Frankfurt</name>
   ....: <jahr aktiv="0">2014</jahr>
   ....: <jahr aktiv="1">2015</jahr>
   ....: </stadt>
   ....: <stadt>
   ....: <name>Köln</name>
   ....: <jahr aktiv="1">2014</jahr>
   ....: <jahr aktiv="1">2015</jahr>
   ....: </stadt>
   ....: </stadtliste>
   ....: '''

In [22]: from lxml import etree

In [23]: root = etree.fromstring(source)

In [24]: root.xpath('.//jahr')
Out[24]: 
[<Element jahr at 0xaabfa8c>,
 <Element jahr at 0xaabfd2c>,
 <Element jahr at 0xaabf4cc>,
 <Element jahr at 0xaabfa2c>,
 <Element jahr at 0xaabf5ac>,
 <Element jahr at 0xaabfbcc>]

In [25]: root.xpath('.//jahr[@aktiv="1"]')
Out[25]: 
[<Element jahr at 0xaabfa8c>,
 <Element jahr at 0xaabfa2c>,
 <Element jahr at 0xaabf5ac>,
 <Element jahr at 0xaabfbcc>]

In [26]: root.xpath('.//jahr[@aktiv="1"]/..')
Out[26]: 
[<Element stadt at 0xaabf94c>,
 <Element stadt at 0xaabfc6c>,
 <Element stadt at 0xaabf74c>]

In [27]: root.xpath('.//jahr[@aktiv="1"]/../name')
Out[27]: 
[<Element name at 0xaabf6cc>,
 <Element name at 0xaabfaec>,
 <Element name at 0xaabfdac>]

In [28]: root.xpath('.//jahr[@aktiv="1"]/../name/text()')
Out[28]: [u'M\xfcnchen', 'Frankfurt', u'K\xf6ln']
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Ok, habe ich so hinbekommen.
Jetzt, wenn das überhaupt möglich ist, würde ich gerne das Resultat so hinbekommen daß ich nur die Städte herausbekomme und alle die 'jahr'-Tags wo ein aktiv="1" vorhanden sind.
(also bei Köln beide SubElemente. Einmal 2014 und einmal 2015)

München 2015
Frankfurt 2015
Köln 2014
Köln 2015

Ich habe eine Schleife die mir alle 'findall'-Elemente durchschleift.

Code: Alles auswählen

for stadt in root.findall(".//jahr[@aktiv='1']/.."):
Ich habe, dank der "/.."-Angabe, nun alle Städte die überhaupt ein Jahr-Element mit aktiv=1 enthalten.
Ich will jetzt an die entsprechenden SubElemente ran. Bei der jetzigen Schleife und der Ausgabe in der Schleife werden mehr als ein Jahrelement einfach ignoriert. Die Bedingung ist ja trotzdem richtig.

Hat jemand einen Tipp wie ich das machen kann falls das überhaupt gehen sollte?
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Ich habe die XML-Konfigurationsdatei bzw. dessen Struktur geändert damit ich sowas wie oben Beschrieben erst gar nicht machen muss. Also: hat sich erledigt.

Vielen Dank an euch Helfer! Echt Top die Hilfe bisher und sicherlich/hoffentlich auch in Zukunft! :wink:
Antworten