geordnete erzeugung von xml mit genshi

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
Benutzeravatar
konus
User
Beiträge: 2
Registriert: Freitag 4. Juli 2008, 21:25

Hallo,
ich bin der Author von http://www.dd4kids.de einer Webseite, die sich der Beschreibung sämlicher Spielplätze in Dresden verschrieben hat. Alle meine Artikel sind mit Geokoordinaten versehen, woraus ich nun eine kml-datei zur Anzeige in Google Earth erzeugen will. Ich habe dazu einige gute Beispiele im Netz gefunden, aber nun bin ich an einer Stelle, an der ich nicht weiterkomme: Ich möchte die xml Ausgabe sortieren bzw. stukturieren.

Ich habe zunächst mittels eine scripts eine CSV-Datei erzeugt, die so aussieht:

Code: Alles auswählen

GroupA, Name1
GroupA, Name2
GroupB, Name3
GroupC, Name4
GroupA, Name5
...
Bisher sieht meine Ausgabe so aus:

Code: Alles auswählen

<Document>
   <Placemark>Name1</Placemark>
   <Placemark>Name2</Placemark>
   <Placemark>Name3</Placemark>
   <Placemark>Name4</Placemark>
 ...
</Document>
Mein Ziel ist eine Ausgabe in der folgenden Art:

Code: Alles auswählen

<Document>
   <Folder id="GroupA">
     <Placemark>Name1</Placemark>
     <Placemark>Name2</Placemark>
     <Placemark>Name5</Placemark>
   </Folder>
   <Folder id="GroupB">
     <Placemark>Name3</Placemark>
   </Folder>
   <Folder id="GroupC">
     <Placemark>Name4</Placemark>
     ...
   </Folder>
</Document>
Das script, welches die xml-Datei erzeugt, sieht so aus (Pfade gekürzt

Code: Alles auswählen

import urllib2, genshi
from genshi.template import TemplateLoader

def collect_items():
    f = urllib2.urlopen('file:csv.txt')
    for line in f:
        fields = line.split(',')
        group1 = fields[0]
        name1  = fields[1]
        yield {
            'group': group1,
            'name': name1,
            }

loader = TemplateLoader(['.'])
template = loader.load('template.xml')
stream = template.generate(collection=collect_items())

f = open('output.kml', 'w')
f.write(stream.render())
Das (vereinfachte) Template dazu sieht so aus:

Code: Alles auswählen

<xml>
  <Document>
     <Placemark py:for="item in collection">
        <name py:content="item['name']">NAME</name>
     </Placemark>
  </Document>
</xml>
Meine Frage ist nun, wie ich dieses Ziel erreichen kann?Ich bitte um Verzeihung, dass gleich mein erstes Posting Überlänge hat, ich wusste nicht, wie ich es kürzer beschreiben soll und mit der Forensuche habe ich nichts passendes entdeckt.

Gruße aus Dresden
konus
lunar

Bei einem solch einfachen Dokument ist ein Template imho überflüssig, da viel zu komplex. Mit einer entsprechenden API kann man das Dokument auch direkt im Code bauen, was kaum länger ist, aber das zusätzliche Template erspart. Auch sehe ich dabei keine Verletzung des Prinzips, Daten von Design zu trennen. Die XML-Datei ist in diesem Fall ja kein "Design", sondern nur eine Repräsentation der Daten, insofern sehe ich nichts verwerfliches darin, das Dokument direkt im Code zu erzeugen.

Dazu ein Beispiel, welches deine CSV-Daten per lxml in ein entsprechendes XML-Dokument umwandelt.

Angeblich ist lxml wesentlich schneller als Genshi, allerdings ist dieses Posting vom (ehemaligen?) lxml-Maintainer, insofern kann man die Objektivität dieses Tests bezweifeln. Ich persönlich habe mit lxml allerdings immer außerordentlich gute Erfahrungen gemacht, und kann die hohe Geschwindigkeit für mich persönlich bestätigen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Die Aufgabe, die tabellarischen Daten in XML zu gruppieren klingt so einfach, dass ich da keine XML-Bibliothek bemühen würde, sondern einfach nur `print` zusammen mit einer Funktion, die das XML-Escape übernimmt. Da ist dann auch völlig egal, ob nun lxml schneller als Genshi ist - ich bin damit schneller fertig als der Download einer entsprechenden Bibliothek ;)

Code: Alles auswählen

print "<Document>"
current_group = None
for line in lines:
    group, name = line.split(",")
    if current_group != group:
        if current_group: print "  </Folder>"
        print "  <Folder id='%s'>" % group
        current_group = group
    print "    <Placemark>%s</Placemark>" % escape(name)
if current_group: print "  </Folder>"
print "</Document>"
Stefan

PS: Die Daten müssen nach Gruppen sortiert sein, damit's klappt.
PPS: DTSTTCPW
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

http://lucumr.pocoo.org/cogitations/tag/wordpress/

XML-Bibliotheken? Pah! Selbst ist der Mann!
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Niemand redet vom Parsen. Es geht um das Erzeugen eines trivialen Formats von Text mit spitzen Klammern.

Parsen kann man prinzipiell auch selbst machen, sollte dann aber um die Beschränkungen von regulären Ausdrücken Bescheid wissen. Damit man da aber nicht zu viel Sonderfälle berücksichtigen muss (XML mit all seinen Encodings und Randfällen kann schnell haarig werden) ist eine fertige Implementierung zu empfehlen.

Echtes XML hat man aber IMHO erst, wenn man Namensräume und/oder DSIG und XENC plus Kanonisierung mit hinzunimmt.

Stefan
lunar

Super :roll: Du gehört dann wohl auch zu denen, die das Rad des XML Parses mit regulären Ausdrücken neu erfinden, weil das "schneller" geht, als lxml mit easy_install zu installieren ...

Diese "XML ist total trivial"-Einstellung ist der Grund, warum die meisten Anwendungen so beschissene XML-Unterstützung haben ...

Und natürlich kann man XML mit regulären Ausdrücken auch selbst parsen ... genauso wie man auch Qt4 neu schreiben kann, um eine GUI-Anwendung zu implementieren.
PPS: DTSTTCPW
Wohl eher BPFH ...
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar hat geschrieben:Super :roll: Du gehört dann wohl auch zu denen, die das Rad des XML Parses mit regulären Ausdrücken neu erfinden, weil das "schneller" geht, als lxml mit easy_install zu installieren ...
Lies von meinen Lippen ab:
sma hat geschrieben:Niemand redet vom Parsen. Es geht um das Erzeugen eines trivialen Formats von Text mit spitzen Klammern.
Und ich bleibe dabei: DTSTTCPW

Stefan
lunar

sma hat geschrieben:
lunar hat geschrieben:Super :roll: Du gehört dann wohl auch zu denen, die das Rad des XML Parses mit regulären Ausdrücken neu erfinden, weil das "schneller" geht, als lxml mit easy_install zu installieren ...
Lies von meinen Lippen ab:
sma hat geschrieben:Niemand redet vom Parsen. Es geht um das Erzeugen eines trivialen Formats von Text mit spitzen Klammern.
Ok, ich hab verstanden. Anstatt irgendwelche zugebenermaßen nicht ganz ernst gemeinten Vergleiche zum Parsen zu ziehen, sage ich dir das nächste Mal einfach direkt ins Gesicht, dass ich deine Einstellung, das Rad neu zu erfinden und XML als trivial anzusehen, für ziemlich dumm erachte.

Btw, schon mal auf die Zeitstempel geschaut? Als ich mein Posting abgeschickt hatte, hatte ich deine Antwort auf audax schlicht und einfach noch nicht gesehen. Insofern war deine Antwort in Stil und Inhalt absolut überflüssig ...
Und ich bleibe dabei: DTSTTCPW
Und es ist also "einfacher", sich um Encoding, Escaping und Einrückung selbst zu kümmern, anstatt "easy_install lxml" auszuführen und dann einer Bibliothek die Arbeit zu überlassen?
Benutzeravatar
konus
User
Beiträge: 2
Registriert: Freitag 4. Juli 2008, 21:25

Hallo, ich möchte mich auch mal wieder zu Wort melden. Vielen Dank erst einmal für die vielen Beiträge und Hinweise! Ich wollte eigentlich mit meinem ersten Posting keinen Glaubenskrieg vom Zaun brechen :twisted:

In der Tat ist das dargestellte XML-Document recht einfach - da liegt aber daran, weil ich alles rausgekürzt habe, welches nicht der Erklärung meines Problems dient. In Wirklichkeit ist es ein ganzes Stück umfangreicher, mit einer Menge an statischen und dynamischen Daten. Ich habe es hier nicht dargestellt, um die Sache nicht zu verkomplizieren. Da ich zu späterer Zeit das KML-File noch weiter aufhübschen will und neben dem KML file auch noch ein GPX-file erzeugen will (das ist ein anderes XML-Format) möchte ich auf jeden Fall bei der Template Lösung bleiben.
Da die Sache einmal pro Tag über ein Cron abgearbeitet wird, ist die Perfomance nicht so wichtig.

Mein Problem ist, dass ich in die ganze Sache etwas reingeschlittert bin und ohne Hello World gleich in Lektion 12 einstgestiegen bin. Ich bin bis hierher per Trial und Error und der Analyse von Beispielen gekommen. Sicher sollte ich noch einmal am Anfang einsteigen um alles zu verstehen, was ich mache.

Mein erster Lösungsansatz sah so aus, im Template für jede Gruppe einen eigenen Abschnitt einzurichten und die Ausgabe mittels py:if="item['gruppe']=='GruppeA'" zu filtern. Für die erste Gruppe funktioniert die Filterung sehr gut, aber leider sind danach meine übergebenen Daten irgendwie aufgebraucht. Ich kann im Template nicht zweimal hintereinander py:for="item in collection" machen. Daher meine Bitte um Tipps zu einer Lösung.
Antworten