Seite 1 von 1

lxml

Verfasst: Mittwoch 13. November 2013, 12:35
von kruphi
Guten Tag,

nachdem ich euren Hinweis/Rat mit der Lib lxml aufgenommen habe, habe ich mein Programm versucht umzuschreiben. Leider komme ich da nicht wie gewünscht mit klar. Ich schätze ich habe wieder nur eine blöden Fehler. Ich bin wie immer für gerechtfertigte Kritik dankbar!

Zu Erklärung: Ich habe eine XML Datei und muss die Daten einlesen. Jede Person wird als Objekt gespeichert.

Code: Alles auswählen

from lxml import etree
class cPersonen:
	def __init__(self, vorname, nachname):
		self.vorname = vorname
		self.nachname = nachname 
tree  = etree.parse("Test.xml")
root = tree.getroot()

all={}
for schueler in root.iter('schueler'):
	nachname = None
	vorname =  None
	for person in schueler.iter('person'):
		for rufname in person.iter('rufname'):
			vorname = rufname.text
		for nachname in person.iter('nachname'):
			nachname = nachname.text
			
		break
	all[schueler]=cPersonen(	vorname, nachname)	
		

Code: Alles auswählen

<daten>
  <schueler>
    <person>
      <rufname>Max</rufname>
      <vorname></vorname>
      <vornamen>Max</vornamen>
      <nachname>Mustermann</nachname>
      <adressen>
        <adresse>
          <ort>
            <name>Entenhausen</name>
            <plz>00000</plz>
          </ort>
          <strasse>Linden 1</strasse>
        </adresse>
      </adressen>
      <bild>
        <text></text>
      </bild>
      <kontakte>
        <kontakt typ="kaTelefon">
          <text>012345678</text>
        </kontakt>
        <kontakt typ="kaTelefon">
          <text>JA: 065646464</text>
        </kontakt>
        <kontakt typ="kaEMail">
          <text>JA:Duck@China.de</text>
        </kontakt>
      </kontakte>
    </person>
    <verantwortliche>
      <verantwortlicher>
        <person>
          <rufname>Mix</rufname>
          <vorname></vorname>
          <vornamen></vornamen>
          <nachname>Mustermann</nachname>
          <geburtsdatum>1982-01-01</geburtsdatum>
          <geschlecht>gWeiblich</geschlecht>
          <adressen>
            <adresse>
              <hauptwohnsitz>true</hauptwohnsitz>
              <ort>
                <name>Entenhause</name>
                <plz>00000</plz>
              </ort>
              <strasse>Linden1</strasse>
            </adresse>
          </adressen>
          <kontakte/>
        </person>
      </verantwortlicher>
    </verantwortliche>
  </schueler>
 </daten>
Ich muss immer eine bestimmte Stelle in der XMl ansprechen. Ist diese Möglichkeit die Beste dafür oder gibt es eine Schlankere Art diese zu erstellen? Vor allem darf ich die Daten von den Mitgliedern und Mentoren nicht durcheinander bekommen.
Ich bin -wie gesagt- dankbar für alle Hinweise! Vielen dank schon mal für eure Antworten!

kruphi

Re: lxml

Verfasst: Mittwoch 13. November 2013, 12:43
von Sirius3
Hallo kruphi,
was heißt "ich komme nicht wie gewünscht klar"?
Ich kann nicht Gedanken lesen und weiß nicht, was Du Dir wünschst.

Re: lxml

Verfasst: Mittwoch 13. November 2013, 12:47
von /me
XPath könnte ein Ansatz sein.

Re: lxml

Verfasst: Mittwoch 13. November 2013, 12:51
von kruphi
Ich suche eine einfachere Methode Atribute einem Objekt zuzuweisen.
Gibt es da einfachere Verfahrensweisen?
Kann ich die Tags wie <kontakt typ=Mail> direkt unterscheiden?

Re: lxml

Verfasst: Mittwoch 13. November 2013, 13:08
von BlackJack
@kruphi: Man sollte nicht für alles die `iter()`-Methode verwenden, sondern nur wenn man tatsächlich mehrere Elemente als Ergebnis erwartet. Ist das bei <schueler> tatsächlich der Fall? Oder bei <person>?

Ansonsten überlege doch bitte mal wie oft die ``for person``-Schleife bei der Datei mit den zwei <person>-Elementen durchlaufen wird. Würde sich daran etwas ändern wenn es mehr <person>-Elemente wären? Wie oft wird ein `cPersonen`-Objekt erstellt? Und was genau ist der Schlüssel für das `all`-Wörterbuch? Welchen Typ und welchen Wert hat der Schlüssel?

Der Klassenname `cPersonen` ist falsch. Zum einen gehört dieses `c` da nicht hin, und zum anderen steht so ein Objekt nur für *eine* Person und nicht für mehrere Person*en*.

Die fürchterliche ungarische Notation bei Werten im XML würde ich auch weglassen. Diese Information kann man aus dem Kontext erkennen. Beziehungsweise braucht man bei "gWeiblich" wohl nicht mal einen Kontext um zu erkennen, dass es sich um eine Geschlechtsangabe handelt.

Der Name `all` ist schon der Name einer eingebauten Funktion, die durch das Wörterbuch verdeckt und damit nicht mehr verwendet werden kann. `all` ist als Name für Daten auch nciht wirklich gut, denn er sagt nicht von *was* dort alles enthalten ist. Es sind alle Schüler (einer Klasse? Schule? Jahrgang? Schulbezirk?) in der Datenstruktur, also würde vielleicht `pupils` passen. Oder wenn der Umstand das es sich um Schüler handelt keine Rolle spielt, dann vielleicht `persons`. Da es sich um ein Wörterbuch handelt werden wird konventionell oft die Bedeutung von Schlüsseln und Werten durch ein `2` oder `_to_` getrennt im Namen kodiert. Also zum Beispiel `id2person`.

Hier wäre mal interessant zu erfahren was Du als Schlüssel bei `all` *eigentlich* hättest nehmen wollen. Da müsste man ja raten. Denn das *was* Du genommen hast, macht gar keinen Sinn.

Re: lxml

Verfasst: Mittwoch 13. November 2013, 13:27
von /me
kruphi hat geschrieben:Ich suche eine einfachere Methode Atribute einem Objekt zuzuweisen.
Gibt es da einfachere Verfahrensweisen?
Kann ich die Tags wie <kontakt typ=Mail> direkt unterscheiden?
Möchtest du Attribute oder Text zuweisen?

Ich habe im Beispielcode mal beides demonstriert.

Code: Alles auswählen

elements = root.xpath('/daten/schueler/person/kontakte/kontakt[@typ="kaEMail"]/text')
# elements = root.xpath('.//kontakt[@typ="kaEMail"]/text')
for element in elements:
    print(element.text)
    element.attrib['changed'] = 'yes'
    element.text = 'me@example.org'
print(etree.tostring(root))

Re: lxml

Verfasst: Mittwoch 13. November 2013, 16:08
von kruphi
@/me
Ich verstehe noch nicht ganz wie das Beispiel nicht.
Ich habe ja mehrere Schueler, aus jedem dieser Schueler möchte ich ein Objekt machen.
Jedes Objekt soll verschieden Atribute besitzen wie z.B.
Vor- und Nachnachme
Eigene Adresse
Aber auch die Daten Daten der Verantwortlichen.


Wenn ich jetzt dein Beispiel nehme, dann bekomme ich doch ein Wörterbuch mit allen Mailadressen oder ?

Re: lxml

Verfasst: Mittwoch 13. November 2013, 16:58
von kruphi
Wenn ich das wie folgt schreibe, schreibt er mir jedes mal die gleichen Werte in das Objekt.
Was muss ich machen damit er bei mir immer in dem entsprechendem Schueler Pfad sucht?

Code: Alles auswählen

for schueler in root.iter('schueler'):
	nachname = None
	vorname =  None
	ident = None
	
	e_vorname=schueler.xpath('/danis/schueler/person/rufname')
	vorname = e_vorname[0].text

	e_nachname=schueler.xpath('/danis/schueler/person/nachname')
	nachname = e_nachname[0].text
	
	e_ident = schueler.xpath('/danis/schueler/identnummer')
	ident = e_ident[0].text
		
	all[ident]=cPersonen(vorname,nachname,ident)

Re: lxml

Verfasst: Mittwoch 13. November 2013, 17:04
von BlackJack
@kruphi: Du musst `schueler` nicht dazu benutzen um einen *globalen* XPath anzugeben sondern einen lokalen der nicht von der Wurzel, sondern von dem <schueler>-Element ausgeht.

Re: lxml

Verfasst: Mittwoch 13. November 2013, 17:08
von kruphi
Auch wenn die Frage dumm sein sollte :K was meinst du mit Global? Ich habe doch gar nichts als global definiert, wie mache ich das Lokal?

Re: lxml

Verfasst: Mittwoch 13. November 2013, 17:19
von BlackJack
@kruphi: Der XPath den Du auf `schueler` angibst ist global/absolut, der geht vom Wurzelelement aus und nicht von dem <schueler>-Element das an `schueler` gebunden ist.

Man könnte sich aber auch die anderen Methoden mal anschauen. Für mehrere Werte die direkte Kinder eine Pfads sind könnte man `findall()` statt `iter()` verwenden und wenn man einen Einzelwert relativ zu einem Element-Objekt haben möchte die `find()`-Methode.

Re: lxml

Verfasst: Donnerstag 14. November 2013, 10:22
von BlackJack
Eine Möglichkeit das sauber aufzuteilen und gezielt Personendaten von Schülern in `Person`-Objekte umzuwandeln:

Code: Alles auswählen

from lxml import etree


class Person(object):
    def __init__(self, given_name, surname, id_):
        self.given_name = given_name
        self.surname = surname
        self.id = id_

    @classmethod
    def from_xml_node(cls, node):
        return cls(
            node.find('rufname').text,
            node.find('nachname').text,
            int(node.find('identnummer').text)
        )


def main():
    document = etree.parse('test.xml')
    persons = map(Person.from_xml_node, document.findall('schueler/person'))
    id2student = dict((person.id, person) for person in persons)
    print(id2student)
        
 
if __name__ == '__main__':
    main()

Re: lxml

Verfasst: Donnerstag 14. November 2013, 15:43
von kruphi
@BlackJack
Danke für deine Hilfe. Leider funktioniert dein Code nicht -oder ich habe da wieder etwas falsch gemacht :oops: -
Ich bekomme folgende Fehlermeldung .
AttributeError: 'NoneType' object has no attribute 'text'

Re: lxml

Verfasst: Donnerstag 14. November 2013, 15:58
von BlackJack
@kruphi: Und dazu bekommst Du einen Traceback der genau auf die Zeile hinweisen sollte in der dieser Fehler auftritt. Dann siehst Du wie das `None`-Objekt zustande kommt auf dem es kein `text`-Attribut gibt. Und dann solltest Du Deine XML-Datei mal betrachten wo dort ein Element fehlt. Es sei denn das Element darf man optional auch weglassen, dann müsste das an der Stelle im Code natürlich entsprechend behandelt werden.