GSP
Quick Navigator

Search Site

Unix VPS
A - Starter
B - Basic
C - Preferred
D - Commercial
MPS - Dedicated
Previous VPSs
* Sign Up! *

Support
Contact Us
Online Help
Handbooks
Domain Status
Man Pages

FAQ
Virtual Servers
Pricing
Billing
Technical

Network
Facilities
Connectivity
Topology Map

Miscellaneous
Server Agreement
Year 2038
Credits
 

USA Flag

 

 

Man Pages


Manual Reference Pages  -  XML::READER_DE (3)

.ds Aq ’

NAME

XML::Reader_de - Lesen von XML-Dateien und Bereitstellung der Pfad information basierend auf einem Pull-Parser.

CONTENTS

UeBERSETZUNG

This 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.pm

Dieses Dokument ist die Deutsche Uebersetzung aus dem Englischen des Moduls XML::Reader. Um den Perl Quelltext des Moduls zu lesen, gehen Sie bitte zur Datei XML/Reader.pm

SYNOPSIS



  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;
  }



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 "Cant XML::Reader->new() because $@";

  if ($rdr) {
      # ... do something with $rdr ...
  }
  else {
      # ... do some error handling ...
  }



BENUTZUNG

Normalerweise 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 auswaehlt:

XML::Reader benutzt XML::Parser als Parser Modul. Das funktioniert sehr gut, ausser in den Faellen in denen kein C-compiler zur verfuegung steht um XML::Parser zu installieren. In diesen Faellen kann XML::Parsepp als eine Perl Alternative zu XML::Parser benutzt werden. Hier ist ein Beispiel:



  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.

BESCHREIBUNG

XML::Reader stellt ein einfach zu bedienendes Interface zur Verfuegung mit dem man XML-Dateien sequentiell lesen kann (sogenanntes pull-mode parsing). Der aktuelle XML-Pfad wird ebenfalls gepflegt.

XML::Reader wurde als eine Huelle ueber dem bestehenden Modul XML::Parser/XML::Parsepp entwickelt (ausserdem wurden einige Grundfunktionen des Moduls XML::TokeParser mit uebernommen). Die bestehenden Module XML::Parser, XML::Parsepp und XML::TokeParser ermoeglichen 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> fuer eine Einfuehrung 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 uebersetzt XML::Reader die in der XML-Datei bestehenden Start-Tags, End-Tags und Text in XPath-aehnliche Ausdruecke, man erhaelt also nur einen Pfad und einen Wert, so einfach ist es. (Sollte man jedoch mit XML::Reader PYX-kompatible Ausdruecke erzeugen wollen, dann kann man das auch mit der Option {filter => 4, mode => ’pyx’}, wie zuvor erwaehnt, erreichen).

Aber kommen wir zurueck 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 naechste 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 moechte, die jeweiligen Start- und End-Tags erkennen: Es existiert die Methode is_start, die genau dann 1 zurueckgibt, wenn an der aktuellen Positon in der XML-Datei ein Start-Tag existiert, ansonsten gibt die Methode 0 zurueck. Es existiert ebenso die zugehoerige Methode is_end, die genau dann 1 zurueckgibt, wenn nach der aktuellen Positon in der XML-Datei ein End-Tag existiert, ansonsten gibt die Methode 0 zurueck.

Es existieren zusaetzlich die Methoden tag, attr, type und level. Die Methode tag liefert den aktuellen Tag-Namen, attr liefert den Attribut-Namen, type liefert entweder ’T’ fuer Text oder ’@’ fuer Attribute, level liefert die im Moment aktive Verschachtelungstiefe (das ist ein numerischer Wert >= 0)

Hier folgend wird, um das Prinzip zu erklaeren, ein Beispielprogramm aufgefuehrt, 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



INTERFACE

    Objekt Erstellung

Um 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 geoeffnetes 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 geoeffneneten 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 koennen als eine Hash-Referenz hinzugefuegt werden:
Option {parse_ct => } Option {parse_ct => 1} ermoeglicht es XML-Kommentare zu lesen, die Voreinstellung ist {parse_ct => 0}
Option {parse_pi => } Option {parse_pi => 1} ermoeglicht es processing-instructions und XML-Declarations zu lesen, die Voreinstellung ist {parse_pi => 0}
Option {using => } Option {using => } ermoeglicht es einen Teil-Baum der XML-Datei zu selektieren.

Die Syntax hierfuer lautet: {using => [’/pfad1/pfad2/pfad3’, ’/pfad4/pfad5/pfad6’]}

Option {filter => } und {mode => } Option {filter => 2} oder {mode => ’attr-bef-start’} zeigt alle XML-Zeilen an, einschliesslich der Attribute.

Option {filter => 3} oder {mode => ’attr-in-hash’} entfernt die Attribut-Zeilen (d.h. alle Zeilen mit $rdr->type eq ’@’ werden entfernt). Anstelle dessen werden die Attribute in einer Hash-Referenz $rdr->att_hash zurueckgeliefert.

Option {filter => 4} oder {mode => ’pyx’} bricht alle Zeilen in individuelle Start-Tags, End-Tags, Attribute, Kommentare und Processing-Instructions auf. Damit wird die Verarbeitung der XML-Datei im PYX-Format ermoeglicht.

Option {filter => 5} oder {mode => ’branches’} selektiert nur die Daten fuer die vorgegebenen Wurzeln (root). Die Datenelemente jeder Wurzel werden in einer Array Referenz (wie spezifiziert durch den branch Parameter) gesammelt und dann zurueckgegeben wenn der branch komplett ist. Diese Vorgehensweise liegt auf halbem Weg zwischen der Option using (wo alle Elemente einzeln zurueckgeliefert werden) und der Funktion slurp_xml (wo alle Elemente in einem branch gesammelt werden, und alle branches dann am Ende in einer grossen Speicherstruktur auf einmal zurueckgeliefert werden).

Die Syntax lautet {filter => 2|3|4|5, mode => ’attr-bef-start’|’attr-in-hash’|’pyx’|’branches’}, die Voreinstellung ist {filter => 2, mode => ’attr-bef-start’}

Option {strip => } Option {strip => 1} entfernt sowohl fuehrende, als auch am Ende befindliche Leerzeichen in Text und in Kommentaren. (Attributen bleiben hiervon unberuehrt). {strip => 0} laesst Text und Kommentare so wie sie in der XML-Datei existieren, mit alle Leerzeichen.

Die Syntax hierfuer lautet {strip => 0|1}, die Voreinstellung ist {strip => 1}

Option {dupatt => } Die Option {dupatt => ’*’} ermoeglicht Attribute, die mehrfach Auftreten, zu verarbeiten. Die verschiedenen Werte werden dann mit dem ’*’-Zeichen zu einem einzelnen Wert verbunden.

    Methoden

Ein erfolgreich erstelltes Objekt vom Typ XML::Reader stellt folgende Methoden zur Verfuegung:
iterate Liest das naechste XML-Element. Die Methode liefert den Wert 1 zurueck wenn das Lesen erfolgreich war, oder undef falls das Ende der XML-Datei erreicht wurde.
path Liefert den gesamten Pfad des aktuellen XML-Elements zurueck, Attribute werden mit einem fuehrenden ’@’-Zeichen markiert.
value Liefert den aktuellen Wert zurueck (d.h. den Wert des aktuellen Textes oder den des aktuellen Attributes).

Bitte beachten Sie dass wenn {filter => 2 oder 3} selektiert wurde und wenn zusaetzlich das aktuelle Element eine XML-Deklaration ist (d.h. $rdr->is_decl == 1), dann ist es ratsam den aktuellen Wert nicht zu beruecksichtigen (er ist dann sowieso leer). Ein typisches Beispiel waere:



  print $rdr->value, "\n" unless $rdr->is_decl;



Dieses oben angegebe Beispiel trifft jedoch nicht zu wenn {filter => 4} aktiv ist. In diesem falle genuegt ein einfaches print $rdr->value;:



  print $rdr->value, "\n";



comment Liefert den aktuellen Kommentar zurueck. Bevor man diesen Wert benutzt, sollte man mit $rdr->is_comment pruefen ob das aktuelle Element wirklich ein Kommentar ist.
type Liefert den Typ des Wertes zurueck: ’T’ fuer Text, ’@’ fuer Attribute.

Falls Option {filter => 4} aktiviert ist, dann kann der Typ folgende Werte annehmen: ’T’ fuer Text, ’@’ fuer Attribute, ’S’ fuer Start-Tags, ’E’ fuer End-Tags, ’#’ fuer Kommentare, ’D’ fuer XML-Declarationen, ’?’ fuer Processing-Instructions.

tag Liefert den aktuellen Tag-Namen zurueck.
attr Liefert den aktuellen Attribut-Namen zurueck (liefert eine leere Zeichenkette zurueck falls das aktuelle Element kein Attribut ist).
level Zeigt die aktuelle Verschachtelungstiefe des XPath-Ausdruckes an (das ist ein numerischer Wert > 0)
prefix Liefert den Praefix zurueck der durch die Option {using => ...} entfernt wurde. Falls die Option {using => ...} nicht benutzt wurde, wird eine leere Zeichenkette zurueckgegeben
att_hash Liefert eine Referenz zu einem Hash zurueck der die aktuellen Attribute eines Start-Tags enthaelt (der Hash ist leer falls der aktuelle Tag kein Start-Tag ist)
dec_hash Liefert eine Referenz zu einem Hash zurueck der die aktuellen Attribute einer XML-Declaration enthaelt (der Hash ist leer falls der aktuelle Tag keine XML-Declaration ist)
proc_tgt Liefert den Ziel Wert (d.h. den ersten Teil) einer Processing-Instruction zurueck (eine leere Zeichenkette wird zurueckgegeben falls der aktuelle Tag keine Processing-Instruction ist)
proc_data Liefert den Daten Wert (d.h. den zweiten Teil) einer Processing-Instruction zurueck (eine leere Zeichenkette wird zurueckgegeben falls der aktuelle Tag keine Processing-Instruction ist)
pyx Liefert eine Zeichenkette im PYX-Format des aktuellen Tags zurueck.

Das PYX-Format ist eine Zeichenkette deren erstes Zeichen eine spezielle Bedeutung hat. Dieses erste Zeichen einer jeweiligen PYX-Zeichenkette gibt den Typ des Ereignisses an mit dem man es zu tun hat: falls das erste Zeichen ein ’(’ ist, dann hat man es mit einem Start-Tag zu tun, wenn es ein ’)’ ist, dann hat man es mit einem End-Tag zu tun, wenn es ein ’A’ ist, dann hat man es mit einem Attribut zu tun, wenn es ein ’-’ ist, dann hat man es mit einem Text zu tun, wenn es ein ’?’ ist, dann hat man es mit einer Processing-Instruction zu tun. (siehe <http://www.xml.com/pub/a/2000/03/15/feature/index.html> fuer eine Einfuehrung in PYX)

Die Methode pyx macht nur Sinn falls die Option {filter => 4} aktiviert wurde, ansonsten wird undef zurueckgeliefert.

is_start Liefert 1 zurueck falls die aktuelle Position ein Start-Tag ist, ansonsten wird 0 zurueckgeliefert.
is_end Liefert 1 zurueck falls die aktuelle Position ein End-Tag ist, ansonsten wird 0 zurueckgeliefert.
is_decl Liefert 1 zurueck falls die aktuelle Position eine XML-Declaration ist, ansonsten wird 0 zurueckgeliefert.
is_proc Liefert 1 zurueck falls die aktuelle Position eine Processing-Instruction ist, ansonsten wird 0 zurueckgeliefert.
is_comment Liefert 1 zurueck falls die aktuelle Position ein Kommentar ist, ansonsten wird 0 zurueckgeliefert.
is_text Liefert 1 zurueck falls die aktuelle Position ein Text ist, ansonsten wird 0 zurueckgeliefert.
is_attr Liefert 1 zurueck falls die aktuelle Position ein Attribut ist, ansonsten wird 0 zurueckgeliefert.
is_value Liefert 1 zurueck falls die aktuelle Position ein Text oder ein Attribut ist, ansonsten wird 0 zurueckgeliefert. Diese Methode ist insbesondere nuetzlich wenn {filter => 4, mode => ’pyx’} aktiv ist, in diesem Falle kann man damit testen ob es sinnvoll ist die Methode value() aufzurufen.
rx Das ist der Index des aktuellen Branches (macht nur Sinn wenn {filter => 5, mode => ’branches’} gesetzt wurde).
rvalue Das ist eine Referenz (auf einen Scalar-Wert oder auf ein Array) fuer den aktuellen Branch. (macht nur Sinn wenn {filter => 5, mode => ’branches’} gesetzt wurde). rvalue is schneller als, aber nicht so einfach zu benutzen wie, die aehnliche funktion value (mit rvalue muss man die De-Referenzierung selber ausfuehren.
rstem Diese Funktion ist identisch mit der bestehenden Funktion path

OPTION USING

Mit 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 laesst dann nur noch Zeilen uebrig, die dann mit ’/pfad1/pfad2/pfad3’ oder mit ’/pfad4/pfad5/pfad6’ beginnen.

Diese Zeilen (die nicht eliminiert wurden) haben dann einen kuerzeren Pfad, weil der Praefix ’/pfad1/pfad2/pfad3’ (oder ’/pfad4/pfad5/pfad6’) entfernt wurde. Der entfernte Praefix 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 muessen, der Begriff komplett bedeutet dass der Pfad intern mit einem anghaengten ’/’-Zeichen abgeschlossen wird.

    Beispiel mit using

Das 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 using

Das 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 Praefix ist leer und der Pfad ist jetzt laenger 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_CT

Die 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_PI

Die 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 waere 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 / MODE

Die Option {filter => } oder {mode => } erlaubt verschiedene Grundeinstellungen zum Verarbeiten von XML-Daten.

    Option filter 2 mode attr-bef-start

Mit der Option {filter => 2} oder {mode => ’attr-bef-start’}, XML::Reader erzeugt eine Zeile fuer 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.

Zusaetzlich, Attribute werden als spezielle Zeilen mit der ’/@...’ Syntax hinzugefuegt.

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 fuer die XML-Struktur ist. Daher duerfen wir diese Zeile nicht vergessen.

Betrachten wir jetzt das selbe Beispiel (mit der Option {filter => 2, mode => ’attr-bef-start’}), jedoch mit einem zusaetzlichen 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-hash

Die Option {filter => 3, mode => ’attr-in-hash’} funktioniert aehnlich 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 fuer 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 fuer {filter => 3}, wir brauchen uns nicht mehr explizit um Attribut-Zeilen zu kuemmern (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 fuer {filter => 3, mode => ’attr-in-hash’} ist identisch mit dem Resultat fuer {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 koennen wir (und sollten wir auch) das schreiben von XML einem anderen Modul ueberlassen. Ich schlage hiermit vor, dass wir das Modul XML::MinWriter benutzen. Hier ist ein Programm welches XML::MinWriter fuer 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 anfaenglich gewoehnungsbeduerftig, es ist jedoch korrektes XML Format.

    Option filter 4 mode pyx

Obwohl es nicht die Hauptfunktion von XML::Reader darstellt, erlaubt die Option {filter => 4, mode => ’pyx’} die Erzeugung von individuellen Zeilen fuer 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 beruecksichtigen 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 fuehrenden 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 gueltig: (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 fuer alle Texte und fuer alle Attribute v=1 gesetzt ist (d.h. $rdr->is_value == 1).

    Option filter 5 mode branches

Mit der Option {filter => 5, mode => ’branches’} spezifiziert man eine (oder mehrere) Wurzeln (root), jede root hat eine Liste von Aesten (branch). Man erhaelt daraufhin genau einen Record fuer jedes Auftreten der root in der XML Struktur. Eine Wurzel kann mit einem einfachen Schraegstrich (z.B. {root => ’/tag1/tag2’}) beginnen, in welchem Falle der Pfad absolut zu betrachten ist, oder sie kann mit einem Doppel-Schraegstrich (z.B. {root => ’//tag1/tag2’}) beginnen, in welchem Falle der Pfad relativ zu betrachten ist. Die Wurzel ist ebenso relativ wenn die Wurzel ohne Schraegstrich beginnt (z.B. {root => ’tag1/tag2’}).

Jeder Record enthaelt 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 zurueck gegeben wird.

Um die Element zu erhalten die in der branch Struktur definiert wurden, kann man die Funktion $rdr->rvalue benutzen.

Am einfachsten laesst sich dieses an einem Beispiel erklaeren:



  use XML::Reader qw(XML::Parser);

  my $line2 = q{
  <data>
    <supplier>ggg</supplier>
    <customer name="orob" 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 fuer den relativen Pfad ’customer’ auslesen. Ausserdem wollen wir den Supplier im absoluten Pfad ’/data/supplier’ lesen. Dann wollen wir fuer den relativen Pfad ’//customer’ den XML-Unterbaum erhalten und schliesslich wollen wir den XML-Unterbaum fuer 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 fuenften 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 zurueckgegeben wird und dass der Parameter {branch => ’+’} zur Folge hat dass eine Liste von PYX Instruktionen zurueckgegeben 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 = orob   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 orob, (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 koennen 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 = orob   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 groesst moeglichste XML-Baum fuer einen relativen Pfad gewaehlt wird. Oder anders ausgedrueckt: die Ausgabe von P: <p>b1</p> and P: <p>b2</p> auf separaten Zeilen ist nicht moeglich da diese Unterbaeme schon in dem groesseren Baum P: <p><p>b1</p><p>b2</p></p> enthalten sind.

OPTION DUPATT

Die Option dupatt erlaubt es mehrere Attribute mit gleichem Namen zu einem einzigen Ergebnis zusammenzufassen. Um dieses zu ermoeglichen, 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 moeglich 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:



BEISPIELE

Betrachten 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-start

Hier ist ein Beispiel-Programm um die XML-Datei mit {filter => 2, mode => ’attr-bef-start’} zu parsen. (Bitte beachten Sie wie der Praefix ’/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 uebertragen, wo sie dann ausgegeben werden koennen.



  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-hash

Mit {filter => 3, mode => ’attr-in-hash’} koennen 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 pyx

Jedoch mit {filter => 4, mode => ’pyx’} wird das Programm wieder komplizierter: Wie schon im Beispiel fuer {filter => 2, mode => ’attr-bef-start’} gezeigt, benoetigen wir hier wieder zwei scalar Variablen (’$p1’ und ’$p3’) um die Parameter in ’/@p1’ und ’/@p3’ aufzunehmen und sie dann zu uebertragen. Zusaetzlich muessen wir hier jedoch auch noch Text-Werte zaehlen koennen (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 koennen.



  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 branches

Sie koennen {filter => 5, mode => ’branches’} und regulaere Ausdruecke 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 Bedingungen

Wir 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>dont 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 hinzufuegen (’/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



FUNKTIONEN

    Funktion slurp_xml

Die 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 fuer 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 naechste Parameter ist die Wurzel des Baumes den wir einlesen wollen (in diesem Falle waere das ’/data/order/database/customer’) mit einer Liste von Elementen die wir, relativ zur Wurzel, selektieren wollen, in diesem Falle waere 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 aehnlich wie XML::Simple, insofern als dass alle benoetigten 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>



AUTOR

Klaus Eichner, March 2009

COPYRIGHT UND LIZENZ

Dieses 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 MODULE

Falls 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, wuerde ich empfehlen die Optionen DATA_MODE=>1 und DATA_INDENT=>2 zu setzen, das erlaubt die korrekte Formatierung und Einrueckung in Ihrer XML Ausgabe-Datei).

REFERENZEN

XML::TokeParser, XML::Simple, XML::Parser, XML::Parsepp, XML::Reader::RS, XML::Reader::PP, XML::Parser::Expat, XML::TiePYX, XML::Writer, XML::MinWriter.
Search for    or go to Top of page |  Section 3 |  Main Index


perl v5.20.3 XML::READER_DE (3) 2014-12-28

Powered by GSP Visit the GSP FreeBSD Man Page Interface.
Output converted with manServer 1.07.