
das Modul XML::Parser
mit einem Perl Programm eine XML Datei so darstellen,
wie die Originaldatei
eine Referenz auf eine Subroutine übergeben
über einen eventorientierten Ansatz eine XML
Datei in eine HTML Datei konvertieren
Wir beschreiben hier nur den eventorientierten
Ansatz zur Verarbeitung von XML Dokumenten. Informationen zu
DOM, XSL, XPATH und die wie diese Technologien mit Perl zusammenarbeiten
finden Sie im Handbuch zu XML siehe link zum XML Handbuch
Wir gehen jetzt von dem XML Dokument aus,
das wir im Kapitel Kurzeinführung
in XML erstellt haben:
<personendaten> <persona><name>Andres Ehmann</name> <telefon>03047301388 </telefon> <beruf>Diplom Volkswirt / Magister Artium</beruf> <adresse>Hallandstrasse 2, 13189 Berlin</adresse> </persona> <persona> <name>Manuel Landivar</name> <telefon>03045654566</telefon> <beruf>Licenciado en letras</beruf> <adresse>Schonhauser Allee 23, 13178 Berlin</adresse> </persona>
<persona><name>Maria Sedlemayer</name>
<telefon>089 49499444</telefon>
<beruf>Rechtanwältin</beruf>
<adresse>Krumme Strasse 5, 456545 Muenchen</adresse>
</persona>
<persona><name>Suleika Isnegrim</name>
<telefon>07623 555844 </telefon>
<beruf>Zahnärztin</beruf>
<adresse>Krozinger Strasse 12, 7867 Freiburg</adresse>
</persona>
</personendaten>
Dieses Dokument wollen wir jetzt mit einem
eventorientierten Ansatz auswerten. Es ist vielleicht nicht
der leistungsfähigste Ansatz, dafür aber ein sehr
verständlicher Ansatz.
Eventorientiert heißt, dass das Perl Skript nach Ereignissen
sucht. Das sind in diesem Zusammenhang: ein öffnender Tag,
der Inhalt des Tags und ein schließender Tag. Hierfür
wird das Perl Modul XML::Parser verwendet. Dieses Modul
ist ein fester Bestandteil der ActiveState Distribution. Es
muss also nicht mit dem Perl Packet Manager, siehe Arbeiten
mit Modulen, eingebunden werden. Wer das Problem für
trivial hält und der Meinung ist, das kann auch ohne ein
Modul mit den Regular Expression funktionieren, der irrt. Der
XML::Parser, der selbst auf expat aufbaut, liest das Dokument
in lightning speed, so dass auch sehr große Dokumente
verarbeitet werden können. Es ist eine andere Kategorie
wie das Auslesen eines Flatfiles.
Ein Skript, welches das XML Dokument ausliest, wird in diesem
Kapitel vorgestellt. (Ein Beispiel für Objektorientierte
Programmierung )
Zuerst kommt ein Beispiel, das lediglich die
Datei so wieder auf den Schirm setzt, wie sie am Anfang aussah.
Anschließend folgt ein komplexeres Beispiel, das tatsächlich
eine Transformation nach HTML vornimmt:
#!/usr/bin/perl
use XML::Parser;
my $zeiger = new XML::Parser ();
$zeiger->setHandlers (Start => \&anfang,End => \&ende,Char=>\&inhalt );
$zeiger->parsefile ("test.xml");
sub anfang
{
$wert_des_zeigers = shift;
$starttag= shift;
print "<$starttag>";
print "\n";
}
sub ende
{
($wert_des_zeigers,$endtag) = @_;
print "$endtag>\n";
}
sub inhalt
{
($wert_des_zeigers,$inhalt)=@_;
print " $inhalt";
}
Um das Skript zum Laufen zu bringen, müssen
sich beide Dateien in einem Ordner befinden. Die XML Datei
sollte hierbei text.xml heißen, weil das Perl Skript
nach einer solchen Datei sucht.
$zeiger->parsefile ("test.xml");
Dieses Skript verwendet das Modul XML::Parser
(das Modul XML und davon den Package Parser).
Dieses Modul wird mit use in das Programm eingebunden. (Unterschied
do, require, use siehe Auslagern von Programmen
mit use):
use XML::Parser
Anschließend wird von dem Package XML::Parser ein Objekt
gebildet (Objektorientierte
Programmierung)
Etwas ungewöhnlich ist hier die Zeile:
my $zeiger
= new XML::Parser ();
Das ist aber das gleiche wie das bereits
bekannte
my $zeiger=XML::Parser ->new();
$zeiger ist jetzt also eine Referenz auf "ein Dingsda",
wahrscheinlich auf einen anonymous Hash, weil die nächste
Zeile einen Hash übergibt. Dieses "referenzierte
Dingsda", wir erinnern uns (Objektorientierte
Programmierung), ist verknüpft mit dem Package XML::Parser.
Die nächste Zeile ist ebenfalls eigentümlich.
$zeiger->setHandlers
(Start => \&anfang,End => \&ende,Char=>\&inhalt
);
Eigentümlich ist die Parameterübergabe:
Übergeben wird ein Hash mit Start, End, Char als Schlüssel
und Referenzen auf die Funktion anfang ,ende, inhalt als Wert.
An einem weiteren Beispiel kann man sich klarmachen, wie dies
funktioniert:
#!/usr/bin/perl
testen("Ehmann"=>"Andres","Maier"=>"Müller","Binsengrün"=>"Igor");
sub testen
{
%banane=@_;
foreach (keys(%banane))
{
print "Nachname ist $_ und Vorname ist $banane{$_} \n";
}
}
%banane=@_;
Der Sonderarray @_, der also aussieht
wie ein Array, kann offensichtlich auch Hashes "verarzten".
Das heißt, auf die oben dargestellte Weise kann ein
Hash vollständig übergeben werden.
Bei Referenzen wurde schon angedeutet,
das nicht nur eine Referenz auf eine Variable, einen Hash
und einen Array gebildet werden kann, sondern auch auf eine
Subroutine.
Mit der folgenden Zeile fangen wir an, das XML Dokument, in
unserem Falle also test.xml, auszuwerten.
$zeiger->parsefile ("test.xml"); Beim
Auswerten stößt der XML Parser auf öffnende
Tags, auf schließende Tags und auf den eigentlichen
Inhalt zwischen den Tags:
<irgendwas>
</irgendwas>
Stößt der XML Parser auf
einen öffnenden Tag, ruft er die Funktion anfang auf
und übergibt zwei Parameter, erstens den Namen des $zeigers
(das referenzierte Dingsda, das weiß zu wem es gehört)
und den Tag, den er gefunden hat.
Der Zeiger interessiert uns nicht. Wir eliminieren ihn mit
shift aus dem Sonderarray @_. Der Tag interessiert uns. Wir
begrenzen mit den Tag Zeichen und setzen ihn auf den Schirm.
Nachdem der XML Parser einen öffnenden Tag gefunden hat,
findet er auch den Inhalt. Folglich wird die Subroutine Inhalt
aufgerufen, der wiederum zwei Parameter übergeben werden:
der Zeiger, der mit shift eliminiert wird, und der Inhalt,
den wir auch ausdrucken.
Das gleiche passiert mit den schließenden Tags.
Wir erhalten also das Original Dokument auf dem Schirm. Zugeben,
das ist noch nicht besonders aufregend. Wie das Dokument aussieht,
wussten wir schon vorher.
Die gleiche Technik kann aber genutzt werde, um ein XML Dokument
in ein HTML Dokument zu konvertieren. Vorher machen wir uns
aber noch mal klar, wie der Aufruf der Subroutinen anfang,
inhalt und start aus dem Objekt heraus erfolgt, bzw. wie er
erfolgen könnte.
| eine
Referenz auf eine Subroutine übergeben |
|
Das unten stehende Programmschnipsel dient
lediglich der Illustrierung, wie eine Referenz auf eine Subroutine
übergeben werden kann.
package test; sub new { $zeiger={};
bless($zeiger); } sub create_love { $zeiger=shift; %werte=@_;
while(($schluessel,$wert)=each(%werte)) { &$wert("$schluessel");
} } sub hello { print "Hallo $_[0] \n"; } sub how_do_you_do
{ print "Ist dein Name $_[0] ?"; } $love=test->
new(); $love->create_love("Knopf"=>\&hello,"Shokufeh"=>\&how_do_you_do);
Entscheidend ist die folgende Zeile. Hier wird mit der gleichen
Syntax, wie in dem Beispiel oben, eine Referenz auf eine Subroutine
und ein Wert übergeben:
$love->create_love("Knopf"=>\&hello,"Shokufeh"=>\&how_do_you_do);
Diese Variablen werden an die Subroutine create_love
übergeben, allerdings nicht als Werte des Objektes, sondern
als globale Variablen. In der folgenden Zeile sehen wir uns
wieder mit der Tatsache konfrontiert, dass @_ erst mal den
Zeiger auf das Objekt überträgt, den wir mit shift
in die Variable $zeiger stecken, und dann wird noch einen
Hash "Knopf"=>\&hello,"Shokufeh"=>\&how_do_you_do) übertragen,
den wir in den Hash %Werte packen:
$zeiger=shift;
%werte=@_;
In der while Schleife rufen wir dann über
die Referenz auf die Subroutine, die Subroutine auf und übergeben
den Schlüssel des Hashs als Parameter.
| über
einen eventorientierten Ansatz die XML Datei in eine HTML
Datei konvertieren |
|
Jetzt kommt ein Perl Skript, das tatsächlich
mit eventorientiertem Ansatz die XML Datei in eine HTML Datei
konvertiert:
#!/usr/bin/perl print
"Content-type:text/html\n\n";
use XML::Parser;
my $zeiger = new XML::Parser ();
$zeiger->setHandlers (
Start => \&anfang,
End => \&ende,Char=>\&inhalt );
$zeiger->parsefile ("test.xml");
print "<html><head><body>";
sub anfang
{
%watnu1=("persona"=>"<table border=1
bgclor=yellow>","name"=>
"<tr><td>","telefon"=>"<td>","beruf"=>"<td>","adresse"=>"<td>");
$wert_des_zeigers = shift;
$starttag=shift;
print $watnu1{$starttag};
print "\n";
}
sub ende
{
%watnu2=("persona"=>"</table>","name"=>
"</td>","telefon"=>"</td>","beruf"=>"</td>","adresse"=>"</td></tr>");
($wert_des_zeigers,$endtag) = @_;
print "$watnu2{$endtag}";
}
sub inhalt
{
($wert_des_zeigers,$inhalt)=@_;
print " $inhalt";
}
print "</body></html>";
Das Programm hat, bis auf einige kleine Änderungen,
den gleichen Aufbau, wie das erste Programm.
Es kann, wie gewohnt, mit der Eingabeaufforderung (Dos Box)
aufgerufen werden. Ein Aufruf im Browser ist ebenfalls möglich,
was hier natürlich mehr Sinn macht, denn in der Dos Box
sind nur die HTML Tags sichtbar. Sie können aber nicht
interpretiert werden. Für den Aufruf im Browser werden
beide Dateien, sowohl das Perl Programm als auch die XML Datei
im cgi-bin Verzeichnis gespeichert.
Der Aufruf erfolgt dann nach dem üblichen Schema: http://127.0.0.1/cgi-bin/mein_Skriptname.pl.
Wir sehen dann, dass für jeden Datensatz eine eigene
Tabelle generiert wird. (eine Tabelle wird für das generiert,
was zwischen folgenden Tags steht)
<persona> </persona>
Das wurde erreicht indem folgende Änderungen
vorgenommen wurden:
der Subroutine anfang wurde dieser Hash
hinzugefügt
%watnu1=("persona"=>"<table
border=1 bgclor=yellow>","name"=>"
<tr><td>","telefon"=>"<td>","beruf"=>"<td>","adresse"=>"<td>");
und bei der Subroutine ende dieser
%watnu2=("persona"=>"</table>","name"=>
"</td>","telefon"=>"</td>","beruf"=>"</td>","adresse"=>"</td></tr>");
Nachdem das Objekt $zeiger die Subroutine parsefile
aufgerufen hat, parst diese die Datei test.xml.
Wenn sie einen öffnenden Tag findet, ruft sie die Subroutine
anfang auf und übergibt zwei Parameter, den Zeiger auf
das Objekt, das sie aufgerufen hat, in diesem Falle $zeiger,
und den Tag, den sie gefunden hat.
Jedem XML Tag wiederum wurden über einen Hash eine Reihe
von HTML Tags zugeordnet. Über diesen Hash wird nun ermittelt,
welche HTML Tags gedruckt werden sollen, wenn der korrespondierende
XML Tag übergeben wird. Diese HTML Tags werden dann gedruckt.
Stößt parsefile auf Inhalt, wird der Inhalt gedruckt.
Das gleiche wie bei start passiert auch bei ende.
Wir verdeutlichen uns das Verfahren noch mal. Die Subroutine
stößt nacheinander auf:
| <personendaten> |
=> keine Verknüpfung, nichts
passiert |
| <persona> |
=><table border=1 bgcolor=yellow> |
| <name> |
=><tr><td> |
| Inhalt |
=>Andres Ehmann |
| </name> |
=></td> |
| <telefon> |
=><td> |
| Inhalt |
=> 03047301388 |
| </telefon> |
=></td> |
| <beruf> |
=><td> |
| Inhalt |
=>Diplom Volkswirt / Magister Artium |
| </beruf> |
=></td> |
| <adresse> |
=><td> |
| Inhalt |
=>Hallandstrasse 2, 13189 Berlin |
| </adresse> |
=></td></tr> |
| <persona> |
=></table> |
| |
|
| etc. |
|
Wir sehen also links den Quellbaum, das XML
Original Dokument und rechts den Ergebnisbaum, die transformierte
Datei.
|
 |