|
NAMEXML::Reader_de - Lesen von XML-Dateien und Bereitstellung der Pfad information basierend auf einem Pull-Parser.ÜBERSETZUNGThis document is the German translation from English of the module XML::Reader. In order to get the Perl source code of the module, please see file XML/Reader.pmDieses Dokument ist die Deutsche Übersetzung aus dem Englischen des Moduls XML::Reader. Um den Perl Quelltext des Moduls zu lesen, gehen Sie bitte zur Datei XML/Reader.pm SYNOPSISuse XML::Reader qw(XML::Parser); my $text = q{<init>n <?test pi?> t<page node="400">m <!-- remark --> r</page></init>}; my $rdr = XML::Reader->new(\$text); while ($rdr->iterate) { printf "Path: %-19s, Value: %s\n", $rdr->path, $rdr->value; } Dieses Programm erzeugt folgendes Resultat: Path: /init , Value: n t Path: /init/page/@node , Value: 400 Path: /init/page , Value: m r Path: /init , Value: Man kann den Aufruf von XML::Reader->new(...) mit einem eval {...} verpacken um zu testen ob der Aufruf erfolgreich war: my $rdr = eval{ XML::Reader->new('test.xml') } or warn "Can't XML::Reader->new() because $@"; if ($rdr) { # ... do something with $rdr ... } else { # ... do some error handling ... } BENUTZUNGNormalerweise benutzt man XML::Reader nicht direkt, sondern man benutzt entweder XML::Reader::RS (welches XML::Parser benutzt) oder man benutzt XML::Reader::PP (welches XML::Parsepp benutzt).Falls man dennoch XML::Reader direkt benutzen will, hier ist die Beschreibung wie man zwischen XML::Parser und XML::Parsepp auswählt: Wenn bei der "use"-Anweisung keine Angabe zum Parser Modul gemacht wird, lädt XML::Reader zunächst XML::Parser. Das funktioniert sehr gut, ausser in den Fällen in denen kein C-compiler zur verfügung steht um XML::Parser zu installieren. Wenn also das Laden von XML::Parser fehlschlägt, wird XML::Parsepp als Ersatz geladen. Es ist auch möglich, den Parser explizit auszuwählen. Im folgenden Beispiel wird XML::Parser ausgewählt, so dass nun die Übersetzung des Moduls fehlschlägt, wenn XML::Parser nicht verfügbar ist. use XML::Reader qw(XML::Parser); my $text = q{<init>n <?test pi?> t<page node="400">m <!-- remark --> r</page></init>}; my $rdr = XML::Reader->new(\$text); while ($rdr->iterate) { printf "Path: %-19s, Value: %s\n", $rdr->path, $rdr->value; } Das einzige was man beachten muss ist dass XML::Reader auf beiden Modulen, XML::Parser und XML::Parsepp, basiert. Das bedeutet konkret dass wenn man XML::Reader ohne XML::Parser installieren will, dann darf man einfach nicht die Tests laufen lassen. BESCHREIBUNGXML::Reader stellt ein einfach zu bedienendes Interface zur Verfügung mit dem man XML-Dateien sequentiell lesen kann (sogenanntes "pull-mode" parsing). Der aktuelle XML-Pfad wird ebenfalls gepflegt.XML::Reader wurde als eine Hülle über dem bestehenden Modul XML::Parser/XML::Parsepp entwickelt (ausserdem wurden einige Grundfunktionen des Moduls XML::TokeParser mit übernommen). Die bestehenden Module XML::Parser, XML::Parsepp und XML::TokeParser ermöglichen beide das sequentielle Verarbeiten von XML-Dateien, jedoch wird in diesen Modulen der XML-Pfad nicht gepflegt. Ausserdem muss man mit den Modulen XML::Parser, XML::Parsepp und XML::TokeParser die Unterscheidung zwischen Start-Tags, End-Tags und Text machen, was meiner Meinung nach die Sache verkompliziert (obwohl man auch dieselbe Situation in XML::Reader simulieren kann, und zwar durch die Option {filter => 4, mode => 'pyx'}, wenn es das ist was man will). Es existiert auch ein Modul namens XML::TiePYX, welches ebenfalls das sequentielle Verarbeiten von XML-Dateien erlaubt (siehe <http://www.xml.com/pub/a/2000/03/15/feature/index.html> für eine Einführung in PYX). Aber dennoch, auch mit XML::TiePYX ist man gezwungen eine Unterscheidung zwischen Start-Tags, End-Tags und Text zu machen und der XML-Pfad wird auch nicht gepflegt. Im Gegensatz dazu übersetzt XML::Reader die in der XML-Datei bestehenden Start-Tags, End-Tags und Text in XPath-ähnliche Ausdrücke, man erhält also nur einen Pfad und einen Wert, so einfach ist es. (Sollte man jedoch mit XML::Reader PYX-kompatible Ausdrücke erzeugen wollen, dann kann man das auch mit der Option {filter => 4, mode => 'pyx'}, wie zuvor erwähnt, erreichen). Aber kommen wir zurück zur normalen Benutzung von XML::Reader, dieses hier ist eine Beispiel XML-Datei, kodiert in der Variablen '$line1': my $line1 = q{<?xml version="1.0" encoding="iso-8859-1"?> <data> <item>abc</item> <item><!-- c1 --> <dummy/> fgh <inner name="ttt" id="fff"> ooo <!-- c2 --> ppp </inner> </item> </data> }; Diese Beispiel XML-Datei kann man mit XML::Reader lesen, und zwar indem man die Methode "iterate" verwendet um das jeweils nächste XML-Element zu lesen. Danach kann man dann mit den Methoden "path" und "value" den Pfad und den aktuellen Wert lesen. Man kann ausserdem, wenn man es denn so möchte, die jeweiligen Start- und End-Tags erkennen: Es existiert die Methode "is_start", die genau dann 1 zurückgibt, wenn an der aktuellen Positon in der XML-Datei ein Start-Tag existiert, ansonsten gibt die Methode 0 zurück. Es existiert ebenso die zugehörige Methode "is_end", die genau dann 1 zurückgibt, wenn nach der aktuellen Positon in der XML-Datei ein End-Tag existiert, ansonsten gibt die Methode 0 zurück. Es existieren zusätzlich die Methoden "tag", "attr", "type" und "level". Die Methode "tag" liefert den aktuellen Tag-Namen, "attr" liefert den Attribut-Namen, "type" liefert entweder 'T' für Text oder '@' für Attribute, "level" liefert die im Moment aktive Verschachtelungstiefe (das ist ein numerischer Wert >= 0) Hier folgend wird, um das Prinzip zu erklären, ein Beispielprogramm aufgeführt, welches die vorangegangene XML-Datei in '$line1' einliest... use XML::Reader qw(XML::Parser); my $rdr = XML::Reader->new(\$line1); my $i = 0; while ($rdr->iterate) { $i++; printf "%3d. pat=%-22s, val=%-9s, s=%-1s, e=%-1s, tag=%-6s, atr=%-6s, t=%-1s, lvl=%2d\n", $i, $rdr->path, $rdr->value, $rdr->is_start, $rdr->is_end, $rdr->tag, $rdr->attr, $rdr->type, $rdr->level; } ...und das hier ist das Resultat: 1. pat=/data , val= , s=1, e=0, tag=data , atr= , t=T, lvl= 1 2. pat=/data/item , val=abc , s=1, e=1, tag=item , atr= , t=T, lvl= 2 3. pat=/data , val= , s=0, e=0, tag=data , atr= , t=T, lvl= 1 4. pat=/data/item , val= , s=1, e=0, tag=item , atr= , t=T, lvl= 2 5. pat=/data/item/dummy , val= , s=1, e=1, tag=dummy , atr= , t=T, lvl= 3 6. pat=/data/item , val=fgh , s=0, e=0, tag=item , atr= , t=T, lvl= 2 7. pat=/data/item/inner/@id , val=fff , s=0, e=0, tag=@id , atr=id , t=@, lvl= 4 8. pat=/data/item/inner/@name, val=ttt , s=0, e=0, tag=@name , atr=name , t=@, lvl= 4 9. pat=/data/item/inner , val=ooo ppp , s=1, e=1, tag=inner , atr= , t=T, lvl= 3 10. pat=/data/item , val= , s=0, e=1, tag=item , atr= , t=T, lvl= 2 11. pat=/data , val= , s=0, e=1, tag=data , atr= , t=T, lvl= 1 INTERFACEObjekt ErstellungUm ein Objekt vom Typ XML::Reader zu erstellen, wird folgende Syntax verwendet:my $rdr = XML::Reader->new($data, {strip => 1, filter => 2, using => ['/path1', '/path2']}); Der Parameter $data (welcher immer mit angegeben werden muss) ist entweder der Name einer XML-Datei, oder eine URL die mit 'http://...' beginnt, oder eine Referenz auf eine Zeichenkette (sodass der Inhalt dieser Zeichenkette als XML verarbeitet werden kann), oder ein zuvor geöffnetes Dateihandle, z.B. \*STDIN, (in diesem Fall wird einfach das Filehandle benutzt um die XML-Daten zu lesen). Hier ist ein Beispiel um ein Objekt des Typs XML::Reader mit einem einfachen Datei-Namen zu erzeugen: my $rdr = XML::Reader->new('input.xml'); Hier ist ein weiteres Beispiel um ein Objekt des Typs XML::Reader mit einer Referenz auf eine Zeichenkette zu erzeugen: my $rdr = XML::Reader->new(\'<data>abc</data>'); Hier ist noch ein weiteres Beispiel um ein Objekt des Typs XML::Reader mit einem zuvor geöffneneten Dateihandle zu erzeugen: open my $fh, '<', 'input.xml' or die "Error: $!"; my $rdr = XML::Reader->new($fh); Hier ist schliesslich ein Beispiel um ein Objekt des Typs XML::Reader mit \*STDIN zu erzeugen: my $rdr = XML::Reader->new(\*STDIN); Eine oder mehrere Optionen können als eine Hash-Referenz hinzugefügt werden:
MethodenEin erfolgreich erstelltes Objekt vom Typ XML::Reader stellt folgende Methoden zur Verfügung:
OPTION USINGMit der Option {using => ...} kann man einen Teil-Baum der XML-Datei selektieren.So funktioniert das im Detail... Die Option {using => ['/pfad1/pfad2/pfad3', '/pfad4/pfad5/pfad6']} eliminiert alle Zeilen deren Pfad nicht mit '/pfad1/pfad2/pfad3' (oder nicht mit '/pfad4/pfad5/pfad6') beginnen. Das lässt dann nur noch Zeilen übrig, die dann mit '/pfad1/pfad2/pfad3' oder mit '/pfad4/pfad5/pfad6' beginnen. Diese Zeilen (die nicht eliminiert wurden) haben dann einen kürzeren Pfad, weil der Präfix '/pfad1/pfad2/pfad3' (oder '/pfad4/pfad5/pfad6') entfernt wurde. Der entfernte Präfix erscheint dann aber in der Methode prefix(). Man kann sagen dass die Pfade '/pfad1/pfad2/pfad3' und '/pfad4/pfad5/pfad6' "absolut" und "komplett" sind. Der Begriff "absolut" bedeutet dass die Pfade mit einem '/' beginnen müssen, der Begriff "komplett" bedeutet dass der Pfad intern mit einem anghängten '/'-Zeichen abgeschlossen wird. Beispiel mit usingDas folgende Programm liest eine XML-Datei und parst sie mit XML::Reader, die Option 'using' selektiert dabei nur einen Teil des XML-Baumes:use XML::Reader qw(XML::Parser); my $line2 = q{ <data> <order> <database> <customer name="aaa" /> <customer name="bbb" /> <customer name="ccc" /> <customer name="ddd" /> </database> </order> <dummy value="ttt">test</dummy> <supplier>hhh</supplier> <supplier>iii</supplier> <supplier>jjj</supplier> </data> }; my $rdr = XML::Reader->new(\$line2, {using => ['/data/order/database/customer', '/data/supplier']}); my $i = 0; while ($rdr->iterate) { $i++; printf "%3d. prf=%-29s, pat=%-7s, val=%-3s, tag=%-6s, t=%-1s, lvl=%2d\n", $i, $rdr->prefix, $rdr->path, $rdr->value, $rdr->tag, $rdr->type, $rdr->level; } Das ist das Ergebnis dieses Programms: 1. prf=/data/order/database/customer, pat=/@name , val=aaa, tag=@name , t=@, lvl= 1 2. prf=/data/order/database/customer, pat=/ , val= , tag= , t=T, lvl= 0 3. prf=/data/order/database/customer, pat=/@name , val=bbb, tag=@name , t=@, lvl= 1 4. prf=/data/order/database/customer, pat=/ , val= , tag= , t=T, lvl= 0 5. prf=/data/order/database/customer, pat=/@name , val=ccc, tag=@name , t=@, lvl= 1 6. prf=/data/order/database/customer, pat=/ , val= , tag= , t=T, lvl= 0 7. prf=/data/order/database/customer, pat=/@name , val=ddd, tag=@name , t=@, lvl= 1 8. prf=/data/order/database/customer, pat=/ , val= , tag= , t=T, lvl= 0 9. prf=/data/supplier , pat=/ , val=hhh, tag= , t=T, lvl= 0 10. prf=/data/supplier , pat=/ , val=iii, tag= , t=T, lvl= 0 11. prf=/data/supplier , pat=/ , val=jjj, tag= , t=T, lvl= 0 Beispiel ohne usingDas folgende Programm liest eine XML-Datei und parst sie mit XML::Reader, jedoch ohne Option 'using'.use XML::Reader qw(XML::Parser); my $rdr = XML::Reader->new(\$line2); my $i = 0; while ($rdr->iterate) { $i++; printf "%3d. prf=%-1s, pat=%-37s, val=%-6s, tag=%-11s, t=%-1s, lvl=%2d\n", $i, $rdr->prefix, $rdr->path, $rdr->value, $rdr->tag, $rdr->type, $rdr->level; } Wie man in dem folgenden Resultat sehen kann, werden mehr Ausgabezeilen geschrieben, der Präfix ist leer und der Pfad ist jetzt länger als zuvor. 1. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 2. prf= , pat=/data/order , val= , tag=order , t=T, lvl= 2 3. prf= , pat=/data/order/database , val= , tag=database , t=T, lvl= 3 4. prf= , pat=/data/order/database/customer/@name , val=aaa , tag=@name , t=@, lvl= 5 5. prf= , pat=/data/order/database/customer , val= , tag=customer , t=T, lvl= 4 6. prf= , pat=/data/order/database , val= , tag=database , t=T, lvl= 3 7. prf= , pat=/data/order/database/customer/@name , val=bbb , tag=@name , t=@, lvl= 5 8. prf= , pat=/data/order/database/customer , val= , tag=customer , t=T, lvl= 4 9. prf= , pat=/data/order/database , val= , tag=database , t=T, lvl= 3 10. prf= , pat=/data/order/database/customer/@name , val=ccc , tag=@name , t=@, lvl= 5 11. prf= , pat=/data/order/database/customer , val= , tag=customer , t=T, lvl= 4 12. prf= , pat=/data/order/database , val= , tag=database , t=T, lvl= 3 13. prf= , pat=/data/order/database/customer/@name , val=ddd , tag=@name , t=@, lvl= 5 14. prf= , pat=/data/order/database/customer , val= , tag=customer , t=T, lvl= 4 15. prf= , pat=/data/order/database , val= , tag=database , t=T, lvl= 3 16. prf= , pat=/data/order , val= , tag=order , t=T, lvl= 2 17. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 18. prf= , pat=/data/dummy/@value , val=ttt , tag=@value , t=@, lvl= 3 19. prf= , pat=/data/dummy , val=test , tag=dummy , t=T, lvl= 2 20. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 21. prf= , pat=/data/supplier , val=hhh , tag=supplier , t=T, lvl= 2 22. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 23. prf= , pat=/data/supplier , val=iii , tag=supplier , t=T, lvl= 2 24. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 25. prf= , pat=/data/supplier , val=jjj , tag=supplier , t=T, lvl= 2 26. prf= , pat=/data , val= , tag=data , t=T, lvl= 1 OPTION PARSE_CTDie Option {parse_ct => 1} erlaubt das Lesen von Kommentaren (normalerweise werden Kommentare von XML::Reader ignoriert, d.h. {parse_ct => 0} ist die Voreinstellung).Hier ist ein Beispiel wo Kommentare, wie voreingestellt, ignoriert werden: use XML::Reader qw(XML::Parser); my $text = q{<?xml version="1.0"?><dummy>xyz <!-- remark --> stu <?ab cde?> test</dummy>}; my $rdr = XML::Reader->new(\$text); while ($rdr->iterate) { if ($rdr->is_decl) { my %h = %{$rdr->dec_hash}; print "Found decl ", join('', map{" $_='$h{$_}'"} sort keys %h), "\n"; } if ($rdr->is_proc) { print "Found proc ", "t=", $rdr->proc_tgt, ", d=", $rdr->proc_data, "\n"; } if ($rdr->is_comment) { print "Found comment ", $rdr->comment, "\n"; } print "Text '", $rdr->value, "'\n" unless $rdr->is_decl; } Hier ist das Ergebnis: Text 'xyz stu test' Jetzt ein Beispiel mit den selben XML-Daten und dem selben Algorithmus, ausser dass die Option {parse_ct => 1} jetzt aktiviert ist: use XML::Reader qw(XML::Parser); my $text = q{<?xml version="1.0"?><dummy>xyz <!-- remark --> stu <?ab cde?> test</dummy>}; my $rdr = XML::Reader->new(\$text, {parse_ct => 1}); while ($rdr->iterate) { if ($rdr->is_decl) { my %h = %{$rdr->dec_hash}; print "Found decl ", join('', map{" $_='$h{$_}'"} sort keys %h), "\n"; } if ($rdr->is_proc) { print "Found proc ", "t=", $rdr->proc_tgt, ", d=", $rdr->proc_data, "\n"; } if ($rdr->is_comment) { print "Found comment ", $rdr->comment, "\n"; } print "Text '", $rdr->value, "'\n" unless $rdr->is_decl; } Hier ist das Ergebnis: Text 'xyz' Found comment remark Text 'stu test' OPTION PARSE_PIDie Option {parse_pi => 1} erlaubt das Lesen von Processing-Instructions und XML-Declarations (normalerweise werden Processing-Instructions und XML-Declarations von XML::Reader ignoriert, d.h. {parse_pi => 0} ist die Voreinstellung).Als Beispiel benutzen wir hier die selben XML-Daten und den selben Algorithmus wie zuvor, ausser dass die Option {parse_pi => 1} aktiviert ist (zusammen mit der schon aktivierten Option {parse_ct => 1}): use XML::Reader qw(XML::Parser); my $text = q{<?xml version="1.0"?><dummy>xyz <!-- remark --> stu <?ab cde?> test</dummy>}; my $rdr = XML::Reader->new(\$text, {parse_ct => 1, parse_pi => 1}); while ($rdr->iterate) { if ($rdr->is_decl) { my %h = %{$rdr->dec_hash}; print "Found decl ", join('', map{" $_='$h{$_}'"} sort keys %h), "\n"; } if ($rdr->is_proc) { print "Found proc ", "t=", $rdr->proc_tgt, ", d=", $rdr->proc_data, "\n"; } if ($rdr->is_comment) { print "Found comment ", $rdr->comment, "\n"; } print "Text '", $rdr->value, "'\n" unless $rdr->is_decl; } Beachten sie im obigen Programm die Zeile "unless $rdr->is_decl". Diese Zeile existiert damit verhindert wird dass der Wert einer XML-Declaration ausgegeben wird (dieser Wert wäre dann sowieso leer). Hier ist das Resultat: Found decl version='1.0' Text 'xyz' Found comment remark Text 'stu' Found proc t=ab, d=cde Text 'test' OPTION FILTER / MODEDie Option {filter => } oder {mode => } erlaubt verschiedene Grundeinstellungen zum Verarbeiten von XML-Daten.Option filter 2 mode attr-bef-startMit der Option {filter => 2} oder {mode => 'attr-bef-start'}, XML::Reader erzeugt eine Zeile für jedes Text-Event. Falls der vorangehende Tag ein Start-Tag ist, dann wird die Methode "is_start" auf 1 gesetzt. Falls der folgende Tag ein End-Tag ist, dann wird die Methode "is_end" auf 1 gesetzt. Falls der vorangehende Tag ein Kommentar ist, dann wird die Methode "is_comment" auf 1 gesetzt. Falls der vorangehende Tag eine XML-Declaration ist, dann wird die Methode "is_decl" auf 1 gesetzt. Falls der vorangehende Tag eine Processing-Instruction ist, dann wird die Methode "is_proc" auf 1 gesetzt.Zusätzlich, Attribute werden als spezielle Zeilen mit der '/@...' Syntax hinzugefügt. Option {filter => 2, mode => 'attr-bef-start'} ist die Voreinstellung. Hier ist ein Beispiel... use XML::Reader qw(XML::Parser); my $text = q{<root><test param='<>v"'><a><b>"e"<data id="<>z'">'g'&<></data>}. q{f</b></a></test>x <!-- remark --> yz</root>}; my $rdr = XML::Reader->new(\$text); # Die folgenden vier Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text); # XML::Reader->new(\$text, {filter => 2 }); # XML::Reader->new(\$text, {filter => 2, mode => 'attr-bef-start'}); # XML::Reader->new(\$text, { mode => 'attr-bef-start'}); while ($rdr->iterate) { printf "Path: %-24s, Value: %s\n", $rdr->path, $rdr->value; } Dieses Programm (mit der impliziten Option {filter => 2, mode => 'attr-bef-start'} als Voreinstellung) produziert folgendes Resultat: Path: /root , Value: Path: /root/test/@param , Value: <>v" Path: /root/test , Value: Path: /root/test/a , Value: Path: /root/test/a/b , Value: "e" Path: /root/test/a/b/data/@id , Value: <>z' Path: /root/test/a/b/data , Value: 'g'&<> Path: /root/test/a/b , Value: f Path: /root/test/a , Value: Path: /root/test , Value: Path: /root , Value: x yz Dieselbe Option {filter => 2, mode => 'attr-bef-start'} erlaubt die Erkennung der XML-Struktur mithilfe der Methoden "is_start" und "is_end". Bitte beachten Sie ebenso in dem obigen Resultat dass die erste Zeile ("Path: /root, Value:") zwar leer ist, jedoch sehr wichtig für die XML-Struktur ist. Daher dürfen wir diese Zeile nicht vergessen. Betrachten wir jetzt das selbe Beispiel (mit der Option {filter => 2, mode => 'attr-bef-start'}), jedoch mit einem zusätzlichen Algorithmus um die originale XML-Struktur wieder herzustellen. Ausserdem verlangen wir das normale Zeichenketten (also keine Tags und auch keine Attribute) mit den Zeichen "** **" markiert werden. use XML::Reader qw(XML::Parser); my $text = q{<root><test param='<>v"'><a><b>"e"<data id="<>z'">'g'&<></data>}. q{f</b></a></test>x <!-- remark --> yz</root>}; my $rdr = XML::Reader->new(\$text); # Die folgenden vier Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text); # XML::Reader->new(\$text, {filter => 2 }); # XML::Reader->new(\$text, {filter => 2, mode => 'attr-bef-start'}); # XML::Reader->new(\$text, { mode => 'attr-bef-start'}); my %at; while ($rdr->iterate) { my $indentation = ' ' x ($rdr->level - 1); if ($rdr->type eq '@') { $at{$rdr->attr} = $rdr->value; for ($at{$rdr->attr}) { s{&}'&'xmsg; s{'}'''xmsg; s{<}'<'xmsg; s{>}'>'xmsg; } } if ($rdr->is_start) { print $indentation, '<', $rdr->tag, join('', map{" $_='$at{$_}'"} sort keys %at), '>', "\n"; } unless ($rdr->type eq '@') { %at = (); } if ($rdr->type eq 'T' and $rdr->value ne '') { my $v = $rdr->value; for ($v) { s{&}'&'xmsg; s{<}'<'xmsg; s{>}'>'xmsg; } print $indentation, " ** $v **\n"; } if ($rdr->is_end) { print $indentation, '</', $rdr->tag, '>', "\n"; } } ...hier ist das Resultat: <root> <test param='<>v"'> <a> <b> ** "e" ** <data id='<>z''> ** 'g'&<> ** </data> ** f ** </b> </a> </test> ** x yz ** </root> ...Dieses Resultat beweist dass die originale XML-Struktur nicht verloren gegangen ist. Option filter 3 mode attr-in-hashDie Option {filter => 3, mode => 'attr-in-hash'} funktioniert ähnlich wie {filter => 2, mode => 'attr-bef-start'}.Der Unterschied jedoch ist dass mit Option {filter => 3, mode => 'attr-in-hash'} alle Attribut-Zeilen eliminiert werden und anstelle dessen die Attribute für ein Start-Tag im hash $rdr->att_hash() erscheinen. Damit wird die Benutzung einer globalen %at-Variable im oben angegebenen Algorithmus nicht mehr notwendig und kann daher durch die Konstruktion %{$rdr->att_hash} erstzt werden. Hier ist ein neuer Algorithmus für {filter => 3}, wir brauchen uns nicht mehr explizit um Attribut-Zeilen zu kümmern (d.h. wir brauchen nicht mehr abzufragen ob $rdr->"type" eq '@') und, wie schon bemerkt, die %at-Variable wird ersetzt durch %{$rdr->"att_hash"} : use XML::Reader qw(XML::Parser); my $text = q{<root><test param='<>v"'><a><b>"e"<data id="<>z'">'g'&<></data>}. q{f</b></a></test>x <!-- remark --> yz</root>}; my $rdr = XML::Reader->new(\$text, {filter => 3}); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text, {filter => 3 }); # XML::Reader->new(\$text, {filter => 3, mode => 'attr-in-hash'}); # XML::Reader->new(\$text, { mode => 'attr-in-hash'}); while ($rdr->iterate) { my $indentation = ' ' x ($rdr->level - 1); if ($rdr->is_start) { my %h = %{$rdr->att_hash}; for (values %h) { s{&}'&'xmsg; s{'}'''xmsg; s{<}'<'xmsg; s{>}'>'xmsg; } print $indentation, '<', $rdr->tag, join('', map{" $_='$h{$_}'"} sort keys %h), '>', "\n"; } if ($rdr->type eq 'T' and $rdr->value ne '') { my $v = $rdr->value; for ($v) { s{&}'&'xmsg; s{<}'<'xmsg; s{>}'>'xmsg; } print $indentation, " ** $v **\n"; } if ($rdr->is_end) { print $indentation, '</', $rdr->tag, '>', "\n"; } } ...das Resultat für {filter => 3, mode => 'attr-in-hash'} ist identisch mit dem Resultat für {filter => 2, mode => 'attr-bef-start'}: <root> <test param='<>v"'> <a> <b> ** "e" ** <data id='<>z''> ** 'g'&<> ** </data> ** f ** </b> </a> </test> ** x yz ** </root> Schliesslich können wir (und sollten wir auch) das schreiben von XML einem anderen Modul überlassen. Ich schlage hiermit vor, dass wir das Modul XML::MinWriter benutzen. Hier ist ein Programm welches XML::MinWriter für die Ausgabe von XML benutzt: use XML::Reader qw(XML::Parser); use XML::MinWriter; my $text = q{<root><test param='<>v"'><a><b>"e"<data id="<>z'">'g'&<></data>}. q{f</b></a></test>x <!-- remark --> yz</root>}; my $rdr = XML::Reader->new(\$text, {filter => 3}); my $wrt = XML::MinWriter->new(OUTPUT => \*STDOUT, NEWLINES => 1); while ($rdr->iterate) { if ($rdr->is_start) { $wrt->startTag($rdr->tag, %{$rdr->att_hash}); } if ($rdr->type eq 'T' and $rdr->value ne '') { $wrt->characters('** '.$rdr->value.' **'); } if ($rdr->is_end) { $wrt->endTag($rdr->tag); } } $wrt->end(); Hier ist das Resultat von XML::MinWriter: <root ><test param="<>v"" ><a ><b >** "e" **<data id="<>z'" >** 'g'&<> **</data >** f **</b ></a ></test >** x yz **</root > Das Ausgabeformat von XML::MinWriter ist anfänglich gewöhnungsbedürftig, es ist jedoch korrektes XML Format. Option filter 4 mode pyxObwohl es nicht die Hauptfunktion von XML::Reader darstellt, erlaubt die Option {filter => 4, mode => 'pyx'} die Erzeugung von individuellen Zeilen für jeweils das Start-Tag, das End-Tag, Kommentare, Processing-Instructions und XML-Declarations. Der Sinn ist eine PYX-kompatible Ausgabe-Zeichenkette zur weiteren Verarbeitung zu erzeugen.Hier ist ein Beispiel: use XML::Reader qw(XML::Parser); my $text = q{<?xml version="1.0" encoding="iso-8859-1"?> <delta> <dim alter="511"> <gamma /> <beta> car <?tt dat?> </beta> </dim> dskjfh <!-- remark --> uuu </delta>}; my $rdr = XML::Reader->new(\$text, {filter => 4, parse_pi => 1}); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text, {filter => 4 , parse_pi => 1}); # XML::Reader->new(\$text, {filter => 4, mode => 'pyx', parse_pi => 1}); # XML::Reader->new(\$text, { mode => 'pyx', parse_pi => 1}); while ($rdr->iterate) { printf "Type = %1s, pyx = %s\n", $rdr->type, $rdr->pyx; } und hier ist das Resultat: Type = D, pyx = ?xml version='1.0' encoding='iso-8859-1' Type = S, pyx = (delta Type = S, pyx = (dim Type = @, pyx = Aalter 511 Type = S, pyx = (gamma Type = E, pyx = )gamma Type = S, pyx = (beta Type = T, pyx = -car Type = ?, pyx = ?tt dat Type = E, pyx = )beta Type = E, pyx = )dim Type = T, pyx = -dskjfh uuu Type = E, pyx = )delta Bitte berücksichtigen Sie dass, falls {parse_ct => 1} gesetzt ist, Kommentare in der Methode "pyx" in einem nicht-standardisierten Format erzeugt werden. Die Kommentare werden dann mit einem führenden Doppelkreuz erzugt welches nicht in der PYX-Spezifikation existiert. Das folgende Beispiel demonstriert diesen Fall: use XML::Reader qw(XML::Parser); my $text = q{ <delta> <!-- remark --> </delta>}; my $rdr = XML::Reader->new(\$text, {filter => 4, parse_ct => 1}); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text, {filter => 4, parse_ct => 1}); # XML::Reader->new(\$text, {filter => 4, mode => 'pyx', parse_ct => 1}); # XML::Reader->new(\$text, { mode => 'pyx', parse_ct => 1}); while ($rdr->iterate) { printf "Type = %1s, pyx = %s\n", $rdr->type, $rdr->pyx; } Hier ist das Ergebnis: Type = S, pyx = (delta Type = #, pyx = #remark Type = E, pyx = )delta Ausserdem, falls {filter => 4, mode => 'pyx'} gesetzt ist, bleiben folgende Methoden gültig: ("value", "attr", "path", "is_start", "is_end", "is_decl", "is_proc", "is_comment", "is_attr", "is_text", "is_value", "comment", "proc_tgt", "proc_data", "dec_hash" und "att_hash"). Hier ist ein Beispiel: use XML::Reader qw(XML::Parser); my $text = q{<?xml version="1.0"?> <parent abc="def"> <?pt hmf?> dskjfh <!-- remark --> <child>ghi</child> </parent>}; my $rdr = XML::Reader->new(\$text, {filter => 4, parse_pi => 1, parse_ct => 1}); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$text, {filter => 4, parse_ct => 1, parse_pi => 1}); # XML::Reader->new(\$text, {filter => 4, mode => 'pyx', parse_ct => 1, parse_pi => 1}); # XML::Reader->new(\$text, { mode => 'pyx', parse_ct => 1, parse_pi => 1}); while ($rdr->iterate) { printf "Path %-15s v=%s ", $rdr->path, $rdr->is_value; if ($rdr->is_start) { print "Found start tag ", $rdr->tag, "\n"; } elsif ($rdr->is_end) { print "Found end tag ", $rdr->tag, "\n"; } elsif ($rdr->is_decl) { my %h = %{$rdr->dec_hash}; print "Found decl ", join('', map{" $_='$h{$_}'"} sort keys %h), "\n"; } elsif ($rdr->is_proc) { print "Found proc ", "t=", $rdr->proc_tgt, ", d=", $rdr->proc_data, "\n"; } elsif ($rdr->is_comment) { print "Found comment ", $rdr->comment, "\n"; } elsif ($rdr->is_attr) { print "Found attribute ", $rdr->attr, "='", $rdr->value, "'\n"; } elsif ($rdr->is_text) { print "Found text ", $rdr->value, "\n"; } } Hier ist das Ergebnis: Path / v=0 Found decl version='1.0' Path /parent v=0 Found start tag parent Path /parent/@abc v=1 Found attribute abc='def' Path /parent v=0 Found proc t=pt, d=hmf Path /parent v=1 Found text dskjfh Path /parent v=0 Found comment remark Path /parent/child v=0 Found start tag child Path /parent/child v=1 Found text ghi Path /parent/child v=0 Found end tag child Path /parent v=0 Found end tag parent Bitte beachten Sie dass für alle Texte und für alle Attribute v=1 gesetzt ist (d.h. $rdr->"is_value" == 1). Option filter 5 mode branchesMit der Option {filter => 5, mode => 'branches'} spezifiziert man eine (oder mehrere) Wurzeln ("root"), jede "root" hat eine Liste von Ästen ("branch"). Man erhält daraufhin genau einen Record für jedes Auftreten der "root" in der XML Struktur. Eine Wurzel kann mit einem einfachen Schrägstrich (z.B. {root => '/tag1/tag2'}) beginnen, in welchem Falle der Pfad absolut zu betrachten ist, oder sie kann mit einem Doppel-Schrägstrich (z.B. {root => '//tag1/tag2'}) beginnen, in welchem Falle der Pfad relativ zu betrachten ist. Die Wurzel ist ebenso relativ wenn die Wurzel ohne Schrägstrich beginnt (z.B. {root => 'tag1/tag2'}).Jeder Record enthält genau die Elemente die in der "branch" Struktur definiert wurden. (Als Sonderfall betrachtet man den Ast {branch => '*'}, in welchem Falle die komplette XML-Strujtur des Unterbaumes zurück gegeben wird. Um die Element zu erhalten die in der "branch" Struktur definiert wurden, kann man die Funktion $rdr->rvalue benutzen. Am einfachsten lässt sich dieses an einem Beispiel erklären: use XML::Reader qw(XML::Parser); my $line2 = q{ <data> <supplier>ggg</supplier> <customer name="o'rob" id="444"> <street>pod alley</street> <city>no city</city> </customer> <customer1 name="troy" id="333"> <street>one way</street> <city>any city</city> </customer1> <tcustomer name="nbc" id="777"> <street>away</street> <city>acity</city> </tcustomer> <supplier>hhh</supplier> <zzz> <customer name='"sue"' id="111"> <street>baker street</street> <city>sidney</city> </customer> </zzz> <order> <database> <customer name="<smith>" id="652"> <street>high street</street> <city>boston</city> </customer> <customer name="&jones" id="184"> <street>maple street</street> <city>new york</city> </customer> <customer name="stewart" id="520"> <street> ring road </street> <city> "'&<A>'" </city> </customer> </database> </order> <dummy value="ttt">test</dummy> <supplier>iii</supplier> <supplier>jjj</supplier> <p> <p>b1</p> <p>b2</p> </p> <p> b3 </p> </data> }; Wir wollen aus der oben angegebenen XML-Datei die Elemente "name", "street" und "city" für den relativen Pfad 'customer' auslesen. Ausserdem wollen wir den Supplier im absoluten Pfad '/data/supplier' lesen. Dann wollen wir für den relativen Pfad '//customer' den XML-Unterbaum erhalten und schliesslich wollen wir den XML-Unterbaum für den relativen Pfad 'p' erhalten. Die Daten der ersten Wurzel 'customer' erkennt man anhand der Bedingung $rdr->rx == 0, die Daten der zweiten Wurzel '/data/supplier' erkennt man anhand der Bedingung $rdr->rx == 1, die Daten der dritten Wurzel '//customer' erkennt man anhand der Bedingung $rdr->rx == 2, die Daten der vierten Wurzel 'p' erkennt man anhand der Bedingung $rdr->rx == 3 und die Daten der fünften Wurzel '//customer' erkennt man anhand der Bedingung $rdr->rx == 4. Bitte beachten Sie in dem folgenden Beispielprogramm dass der Parameter {branch => '*'} zur Folge hat dass eine XML Zeichenkette zurückgegeben wird und dass der Parameter {branch => '+'} zur Folge hat dass eine Liste von PYX Instruktionen zurückgegeben wird. Im folgenden Programm benutzen wir die Funktion $rdr->rvalue um die Daten zu erhalten: my $rdr = XML::Reader->new(\$line2, {filter => 5}, { root => 'customer', branch => ['@name', 'street', 'city'] }, { root => '/data/supplier', branch => ['/'] }, { root => '//customer', branch => '*' }, { root => 'p', branch => '*' }, { root => '//customer', branch => '+' }, ); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$line2, {filter => 5, }); # XML::Reader->new(\$line2, {filter => 5, mode => 'branches'}); # XML::Reader->new(\$line2, { mode => 'branches'}); my $root0 = ''; my $root1 = ''; my $root2 = ''; my $root3 = ''; my $root4 = ''; my $path0 = ''; while ($rdr->iterate) { if ($rdr->rx == 0) { $path0 .= " ".$rdr->path."\n"; for ($rdr->rvalue) { $root0 .= sprintf " Cust: Name = %-7s Street = %-12s City = %s\n", $_->[0], $_->[1], $_->[2]; } } elsif ($rdr->rx == 1) { for ($rdr->rvalue) { $root1 .= " Supp: Name = $_->[0]\n"; } } elsif ($rdr->rx == 2) { for ($rdr->rvalue) { $root2 .= " Xml: $_\n"; } } elsif ($rdr->rx == 3) { for ($rdr->rvalue) { $root3 .= " P: $_\n"; } } elsif ($rdr->rx == 4) { for ($rdr->rvalue) { local $" = "', '"; $root4 .= " Pyx: '@$_'\n"; } } } print "root0:\n$root0\n"; print "path0:\n$path0\n"; print "root1:\n$root1\n"; print "root2:\n$root2\n"; print "root3:\n$root3\n"; print "root4:\n$root4\n"; Hier ist das Ergebnis: root0: Cust: Name = o'rob Street = pod alley City = no city Cust: Name = "sue" Street = baker street City = sidney Cust: Name = <smith> Street = high street City = boston Cust: Name = &jones Street = maple street City = new york Cust: Name = stewart Street = ring road City = "'&<A>'" path0: /data/customer /data/zzz/customer /data/order/database/customer /data/order/database/customer /data/order/database/customer root1: Supp: Name = ggg Supp: Name = hhh Supp: Name = iii Supp: Name = jjj root2: Xml: <customer id='444' name='o'rob'><street>pod alley</street><city>no city</city></customer> Xml: <customer id='111' name='"sue"'><street>baker street</street><city>sidney</city></customer> Xml: <customer id='652' name='<smith>'><street>high street</street><city>boston</city></customer> Xml: <customer id='184' name='&jones'><street>maple street</street><city>new york</city></customer> Xml: <customer id='520' name='stewart'><street>ring road</street><city>"'&<A>'"</city></customer> root3: P: <p><p>b1</p><p>b2</p></p> P: <p>b3</p> root4: Pyx: '(customer', 'Aid 444', 'Aname o'rob', '(street', '-pod alley', ')street', '(city', '-no city', ')city', ')customer' Pyx: '(customer', 'Aid 111', 'Aname "sue"', '(street', '-baker street', ')street', '(city', '-sidney', ')city', ')customer' Pyx: '(customer', 'Aid 652', 'Aname <smith>', '(street', '-high street', ')street', '(city', '-boston', ')city', ')customer' Pyx: '(customer', 'Aid 184', 'Aname &jones', '(street', '-maple street', ')street', '(city', '-new york', ')city', ')customer' Pyx: '(customer', 'Aid 520', 'Aname stewart', '(street', '-ring road', ')street', '(city', '-"'&<A>'"', ')city', ')customer' Wir können ebenso die Funktion $rdr->value benutzen um die gleichen Daten zu erhalten: my $rdr = XML::Reader->new(\$line2, {filter => 5}, { root => 'customer', branch => ['@name', 'street', 'city'] }, { root => 'p', branch => '*' }, ); # Die folgenden drei Alternativen sind gleichwertig: # -------------------------------------------------- # XML::Reader->new(\$line2, {filter => 5, }); # XML::Reader->new(\$line2, {filter => 5, mode => 'branches'}); # XML::Reader->new(\$line2, { mode => 'branches'}); my $out0 = ''; my $out1 = ''; while ($rdr->iterate) { if ($rdr->rx == 0) { my @rv = $rdr->value; $out0 .= sprintf " Cust: Name = %-7s Street = %-12s City = %s\n", $rv[0], $rv[1], $rv[2]; } elsif ($rdr->rx == 1) { $out1 .= " P: ".$rdr->value."\n"; } } print "output0:\n$out0\n"; print "output1:\n$out1\n"; Das is das Ergebnis: output0: Cust: Name = o'rob Street = pod alley City = no city Cust: Name = "sue" Street = baker street City = sidney Cust: Name = <smith> Street = high street City = boston Cust: Name = &jones Street = maple street City = new york Cust: Name = stewart Street = ring road City = "'&<A>'" output1: P: <p><p>b1</p><p>b2</p></p> P: <p>b3</p> Es ist hier zu Bemerken dass der Fall "root3" / "output1" { root => 'p', branch => '*' } beweist dass immer der grösst möglichste XML-Baum für einen relativen Pfad gewählt wird. Oder anders ausgedrückt: die Ausgabe von "P: <p>b1</p>" and "P: <p>b2</p>" auf separaten Zeilen ist nicht möglich da diese Unterbäme schon in dem grösseren Baum "P: <p><p>b1</p><p>b2</p></p>" enthalten sind. OPTION DUPATTDie Option dupatt erlaubt es mehrere Attribute mit gleichem Namen zu einem einzigen Ergebnis zusammenzufassen. Um dieses zu ermöglichen, werden die verschiedenen Werte zu einem einzigen Wert und durch ein spezielles Trennzeichen verbunden. Die Attributliste wird in diesem Falle alphabetisch sortiert ausgegeben.Die Option dupatt ist nur dann möglich wenn XML::Reader mit der Option XML::Parsepp eingebunden wurde. Das folgende Beispiel zeigt die Verwendung der Option dupatt: (Der Verbindungs String {dupatt => $str} ist begrenzt auf druckbare Ascii-Zeichen ausser alphanumerische Zeichen, " oder ') use XML::Reader qw(XML::Parser); my $text = q{<data><item a2="abc" a1="def" a2="ghi"></item></data>} my $rdr = XML::Reader->new(\$text, {dupatt => '|'}); while ($rdr->iterate) { printf "Path: %-19s, Value: %s\n", $rdr->path, $rdr->value; } Dieses Programm erzeugt die folgende Ausgabe: Path: /data , Value: Path: /data/item/@a1 , Value: def Path: /data/item/@a2 , Value: abc|ghi Path: /data/item , Value: Path: /data , Value: BEISPIELEBetrachten wir nun folgende XML-Datei, von der wir die Werte innerhalb der Tags <item> (dabei meine ich nur den ersten Teil 'start...', und nicht den zweiten Teil 'end...') plus die Attribute "p1" und "p3" extrahieren wollen. Der <item>-Tag muss exact im Pfad /start/param/data existieren (und *nicht* im Pfad /start/param/dataz).my $text = q{ <start> <param> <data> <item p1="a" p2="b" p3="c">start1 <inner p1="p">i1</inner> end1</item> <item p1="d" p2="e" p3="f">start2 <inner p1="q">i2</inner> end2</item> <item p1="g" p2="h" p3="i">start3 <inner p1="r">i3</inner> end3</item> </data> <dataz> <item p1="j" p2="k" p3="l">start9 <inner p1="s">i9</inner> end9</item> </dataz> <data> <item p1="m" p2="n" p3="o">start4 <inner p1="t">i4</inner> end4</item> </data> </param> </start>}; Wir erwarten genau 4 Ergebnis-Zeilen von unserem Parse-Lauf (d.h. wir erwarten keine 'dataz' - 'start9' Werte): item = 'start1', p1 = 'a', p3 = 'c' item = 'start2', p1 = 'd', p3 = 'f' item = 'start3', p1 = 'g', p3 = 'i' item = 'start4', p1 = 'm', p3 = 'o' Beispiel XML filter 2 mode attr-bef-startHier ist ein Beispiel-Programm um die XML-Datei mit {filter => 2, mode => 'attr-bef-start'} zu parsen. (Bitte beachten Sie wie der Präfix '/start/param/data/item' in der {using => ...} Option von new verwendet wird). Wir brauchen ausserdem zwei scalar Variablen ('$p1' und '$p3') um die Parameter in '/@p1' und '/@p3' aufzunehmen und sie in den Programmteil $rdr->is_start zu übertragen, wo sie dann ausgegeben werden können.my $rdr = XML::Reader->new(\$text, {mode => 'attr-bef-start', using => '/start/param/data/item'}); my ($p1, $p3); while ($rdr->iterate) { if ($rdr->path eq '/@p1') { $p1 = $rdr->value; } elsif ($rdr->path eq '/@p3') { $p3 = $rdr->value; } elsif ($rdr->path eq '/' and $rdr->is_start) { printf "item = '%s', p1 = '%s', p3 = '%s'\n", $rdr->value, $p1, $p3; } unless ($rdr->is_attr) { $p1 = undef; $p3 = undef; } } Beispiel filter 3 mode attr-in-hashMit {filter => 3, mode => 'attr-in-hash'} können wir auf die zwei scalar Variablen ('$p1' und '$p3') verzichten, das Programm vereinfacht sich wie folgt:my $rdr = XML::Reader->new(\$text, {mode => 'attr-in-hash', using => '/start/param/data/item'}); while ($rdr->iterate) { if ($rdr->path eq '/' and $rdr->is_start) { printf "item = '%s', p1 = '%s', p3 = '%s'\n", $rdr->value, $rdr->att_hash->{p1}, $rdr->att_hash->{p3}; } } Beispiel filter 4 mode pyxJedoch mit {filter => 4, mode => 'pyx'} wird das Programm wieder komplizierter: Wie schon im Beispiel für {filter => 2, mode => 'attr-bef-start'} gezeigt, benötigen wir hier wieder zwei scalar Variablen ('$p1' und '$p3') um die Parameter in '/@p1' und '/@p3' aufzunehmen und sie dann zu übertragen. Zusätzlich müssen wir hier jedoch auch noch Text-Werte zählen können (siehe scalar Variable '$count'), sodass wir zwischen dem ersten Wert 'start...' (welchen wir ausgeben wollen) und dem zweiten Wert 'end...' (welchen wir nicht ausgeben wollen) unterscheiden können.my $rdr = XML::Reader->new(\$text, {mode => 'pyx', using => '/start/param/data/item'}); my ($count, $p1, $p3); while ($rdr->iterate) { if ($rdr->path eq '/@p1') { $p1 = $rdr->value; } elsif ($rdr->path eq '/@p3') { $p3 = $rdr->value; } elsif ($rdr->path eq '/') { if ($rdr->is_start) { $count = 0; $p1 = undef; $p3 = undef; } elsif ($rdr->is_text) { $count++; if ($count == 1) { printf "item = '%s', p1 = '%s', p3 = '%s'\n", $rdr->value, $p1, $p3; } } } } Beispiel filter 5 mode branchesSie können {filter => 5, mode => 'branches'} und reguläre Ausdrücke lombinieren:my $rdr = XML::Reader->new(\$text, {mode => branches}, { root => '/start/param/data/item', branch => '*' }); while ($rdr->iterate) { if ($rdr->value =~ m{\A <item (?:\s+ p1='([^']*)')? (?:\s+ p2='([^']*)')? (?:\s+ p3='([^']*)')? \s* > ([^<]*) <}xms) { printf "item = '%s', p1 = '%s', p3 = '%s'\n", $4, $1, $3; } } Beispiel Attribut BedingungenWir betrachten nun ein weiteres Beispiel ($text3):my $text3 = q{ <data> <database loc="alpha"> <item> <customer name="smith" id="652"> <street>high street</street> <city>rio</city> </customer> <customer name="jones" id="184"> <street>maple street</street> <city>new york</city> </customer> <customer name="gates" id="520"> <street>ring road</street> <city>dallas</city> </customer> <customer name="smith" id="800"> <street>which way</street> <city>ny</city> </customer> </item> </database> <database loc="beta"> <item> <customer name="smith" id="001"> <street>nowhere</street> <city>st malo</city> </customer> <customer name="jones" id="002"> <street>all the way</street> <city>leeds</city> </customer> <customer name="gates" id="003"> <street>bypass</street> <city>rome</city> </customer> </item> </database> <database loc="alpha"> <item> <customer name="peter" id="444"> <street>upton way</street> <city>motown</city> </customer> <customer name="gates" id="959"> <street>don't leave me this way</street> <city>cambridge</city> </customer> </item> </database> <database loc="alpha"> <item> <customer name="smith" id="881"> <street>anyway</street> <city>big apple</city> </customer> <customer name="thatcher" id="504"> <street>baker street</street> <city>oxford</city> </customer> </item> </database> </data> }; Jetzt kann man in den Pfaden (root => ... and branch => ...) Attribut Bedingungen hinzufügen ('/path1[@attr="val"]/...'), z.B.: my $rdr = XML::Reader->new(\$text3, {mode => 'branches', sepchar => '|'}, { root => '/data/database[@loc="alpha"]', branch => [ 'item/customer[@name="smith"]/city', 'item/customer[@name="gates"]/city', ]}); while ($rdr->iterate) { my ($smith, $gates) = $rdr->value; $smith = defined($smith) ? "'$smith'" : 'undef'; $gates = defined($gates) ? "'$gates'" : 'undef'; printf "smith = %-12s, gates = %s\n", $smith, $gates; } Das Ergebnis sieht wie folgt aus: smith = 'rio|ny' , gates = 'dallas' smith = undef , gates = 'cambridge' smith = 'big apple' , gates = undef FUNKTIONENFunktion slurp_xmlDie Funktion slurp_xml liest eine XML Datei und legt die Daten in einer Array-Referenz ab. Hier ist ein Beispiel in dem wir den Namen, die Strasse und die Stadt für alle Kunden im Pfad '/data/order/database/customer' erhalten wollen. Wir wollen ausserdem alle Supplier im Pfad '/data/supplier' erhalten:use XML::Reader qw(XML::Parser slurp_xml); my $line2 = q{ <data> <supplier>ggg</supplier> <supplier>hhh</supplier> <order> <database> <customer name="smith" id="652"> <street>high street</street> <city>boston</city> </customer> <customer name="jones" id="184"> <street>maple street</street> <city>new york</city> </customer> <customer name="stewart" id="520"> <street>ring road</street> <city>dallas</city> </customer> </database> </order> <dummy value="ttt">test</dummy> <supplier>iii</supplier> <supplier>jjj</supplier> </data> }; my $aref = slurp_xml(\$line2, { root => '/data/order/database/customer', branch => ['@name', 'street', 'city'] }, { root => '/data/supplier', branch => '*' }, ); for (@{$aref->[0]}) { printf "Cust: Name = %-7s Street = %-12s City = %s\n", $_->[0], $_->[1], $_->[2]; } print "\n"; for (@{$aref->[1]}) { printf "S: %s\n", $_; } Der erste Parameter in slurp_xml ist entweder der Dateiname (oder eine Skalar Referenz, oder ein offenes Dateihandle) der XML Datei die wir einlesen wollen. In diesem Fall lesen wir von der Skalar Referenz \$line2. Der nächste Parameter ist die Wurzel des Baumes den wir einlesen wollen (in diesem Falle wäre das '/data/order/database/customer') mit einer Liste von Elementen die wir, relativ zur Wurzel, selektieren wollen, in diesem Falle wäre das ['@name', 'street', 'city']. Der darauffolgende Parameter ist eine zweite Wurzel (root/branch Definition), in diesem Falle ist es root => '/data/supplier' mit branch => ['/']. Hier ist das Resultat: Cust: Name = smith Street = high street City = boston Cust: Name = jones Street = maple street City = new york Cust: Name = stewart Street = ring road City = dallas S: <supplier>ggg</supplier> S: <supplier>hhh</supplier> S: <supplier>iii</supplier> S: <supplier>jjj</supplier> slurp_xml funktioniert ähnlich wie XML::Simple, insofern als dass alle benötigten Informationen in einem Rutsch in einer Speicherstruktur abgelegt werden. Der Unterschied jedoch ist dass slurp_xml uns erlaubt spezifische Daten zu selektieren bevor das Einlesen beginnt. Dieses hat zur Folge dass die sich ergebende Speicherstruktur meistens kleiner ist, und damit auch weniger kompliziert. Man kann slurp_xml auch beauftragen doppelte Attribute zu verwalten. In diesem Falle muss man zwei Dinge beachten: Erstens muss man ein "use XML::Reader" mit folgenden optionen: "qw(XML::Parsepp slurp_xml)" ausfuehren. Zum zweiten muss man slurp_xml mit der option { dupatt => '|' } aufrufen, wie das folgende Beispiel belegt: use XML::Reader qw(XML::Parser slurp_xml); my $line3 = q{<data atr1='abc' atr2='def' atr1='ghi'></data>}; my $aref = slurp_xml(\$line3, { dupatt => '|' }, { root => '/', branch => '*' }); print $aref->[0][0], "\n"; Hier ist das Ergebnis: <data atr1='abc|ghi' atr2='def'></data> AUTORKlaus Eichner, March 2009COPYRIGHT UND LIZENZDieses ist der original Text auf Englisch:Copyright (C) 2009 by Klaus Eichner. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the artistic license 2.0, see http://www.opensource.org/licenses/artistic-license-2.0.php VERWANDTE MODULEFalls Sie vorhaben XML auch ausgeben zu wollen, dann schlage ich vor eines der zwei Module XML::Writer oder XML::MinWriter zu benutzen. Beide Module bieten ein einfach zu handhabendes Interface zur Ausgabe von XML-Dateien. (Falls sie non-mixed content XML ausgeben, würde ich empfehlen die Optionen DATA_MODE=>1 und DATA_INDENT=>2 zu setzen, das erlaubt die korrekte Formatierung und Einrückung in Ihrer XML Ausgabe-Datei).REFERENZENXML::TokeParser, XML::Simple, XML::Parser, XML::Parsepp, XML::Reader::RS, XML::Reader::PP, XML::Parser::Expat, XML::TiePYX, XML::Writer, XML::MinWriter.
Visit the GSP FreeBSD Man Page Interface. |