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_FR (3)

.ds Aq ’

NAME

XML::Reader_fr - Lire du XML avec des informations du chemin, conduit par un parseur d’extraction.

CONTENTS

TRADUCTION

This document is the French 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

Ce document est une traduction Franc\k:,aise de l’Anglais du module XML::Reader. Pour obtenir la source Perl du module, consultez le fichier 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;
  }



Ce programme creé le re\k:'suivant:



  Path: /init              , Value: n t
  Path: /init/page/@node   , Value: 400
  Path: /init/page         , Value: m r
  Path: /init              , Value:



On peut toujours rajouter un eval {...} autours d’un appel XML::Reader->new(...) afin de ve\k:'\h |\n:urifier que l’appel a reússi:



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



UTILISATION

Normalement, on n’utilise pas XML::Reader tout seul, on utilise pluto\k:^t XML::Reader::RS (qui utilise XML::Parser) ou on utilise XML::Reader::PP (qui utilise XML::Parsepp).

Par contre, si on veut utiliser XML::Reader directement, voici la proce\k:'pour choisir entre XML::Parser et XML::Parsepp:

XML::Reader utilise XML::Parser comme sous-module. Cette configuration marche tre\k:`s bien, sauf dans le cas ou\k:` il n’y a pas de compilateur C disponible pour installer XML::Parser. Dans ce cas, XML::Parsepp peut remplacer XML::Parser. Voici un exemple:



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



La seule chose est que XML::Reader de\k:'des deux modules XML::Parser et XML::Parsepp. En conse\k:'\h |\n:uquence, si vous ne pouvez pas installer XML::Parser, vous pouvez quand me\k:^me installer XML::Reader, mais vous ne lancez pas les tests.

DESCRIPTION

XML::Reader est un module simple et facile a\k:` utiliser pour parser des fichiers XML de manie\k:`re se\k:'\h |\n:uquentielle (aussi appelle\k:'parseur guide\k:'par l’extraction) et, en me\k:^me temps, il enregistre le chemin complet du XML.

Il a e\k:'|\n:u de\k:'|\n:u comme une couche sur XML::Parser/XML::Parsepp (quelques fonctionalite\k:'basiques ont e\k:'|\n:u copie\k:'de XML::TokeParser). XML::Parser, XML::Parsepp et XML::TokeParser utilisent chacun une me\k:'d’extraction se\k:'\h |\n:uquentielle, mais ils n’enregistrent pas le chemin du XML.

De plus, avec les interfaces de XML::Parser, XML::Parsepp et XML::TokeParser, on est oblige\k:'de se\k:'les balises de de\k:'\h |\n:ubut, les balises de fin et du texte, ce qui, a\k:` mon avis, rend l’utilisation assez complique\k:'(par contre, si on le souhaite, XML::Reader peut agir d’une manie\k:`re a\k:` ce que les balises de de\k:'les balises de fin et du texte sont se\k:'|\n:us, par l’option {filter => 4, mode => ’pyx’}).

Il y a aussi XML::TiePYX, qui permet de parser des fichiers XML de manie\k:`re se\k:'(voir <http://www.xml.com/pub/a/2000/03/15/feature/index.html> pour consulter une introduction a\k:` PYX). Mais me\k:^me avec XML::TiePYX, il faut se\k:'les balises de de\k:'les balises de fin et le texte, et il n’y a pas de chemin disponible.

Par contre, avec XML::Reader, les les balises de de\k:'les balises de fin et le texte sont traduits en expressions similaires a\k:` XPath. En conse\k:'il est inutile de compter des balises individuelles, on a un chemin et une valeur, et c\k:,a suffit. (par contre, au cas ou\k:` on veut ope\k:'XML::Reader en fonctionnement compatible a\k:` PYX, il y a toujours l’option {filter => 4, mode => ’pyx’}, comme de\k:'mentionne\k:'ci-dessus).

Mais revenons-nous au fonctionnement normal de XML::Reader, voici un exemple XML dans la variable ’$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>
  };



Cet exemple peut e\k:^tre parse\k:'avec XML::Reader en utilisant les me\k:'iterate pour lire se\k:'\h |\n:uquentiellement les donneés XML, et en utilisant les me\k:'path et value pour extraire le chemin et la valeur a\k:` un endroit pre\k:'dans le fichier XML.

Si ne\k:'on peut e\k:'identifier les balises individuelles de de\k:'et de fin: Il y a une me\k:'\h |\n:uthode is_start, qui donne 1 ou 0 (c’est a\k:` dire: 1, s’il y a une balise de de\k:'a\k:` la position actuelle, sinon 0). Il y a e\k:'la me\k:'e\k:'is_end.

En plus, il y a les me\k:'tag, attr, type and level. tag retourne le nom de la balise en cours, attr retourne l’identifiant d’un attribut, type retourne ’T’ s’il y a du texte, ou ’@’ s’il y a des attributs et level indique le niveau de cascadage (un nombre >= 0)

Voici un programme qui lit le XML dans la variable ’$line1’ (voir ci-dessus) pour montrer le principe...



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



...et voici le re\k:'\h |\n:usultat:



   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

    Creátion objet

Pour creér un objet, on utilise la syntaxe suivante:



  my $rdr = XML::Reader->new($data,
    {strip => 1, filter => 2, using => [/path1, /path2]});



L’e\k:'|\n:ument $data est obligatoire, il est le nom d’un fichier XML, ou une URL qui commence par ’http://...’, ou la re\k:'|\n:urence a\k:` une chai\k:^ne de caracte\k:`res, dans ce cas le contenu de cette chai\k:^ne de caracte\k:`res est accepte\k:'comme XML.

Sinon, $data peut e\k:'e\k:^tre une re\k:'|\n:urence a\k:` un fichier, comme par exemple \*STDIN. Dans ce cas, la re\k:'|\n:urence de fichier est utiliser pour lire le XML.

Voici un exemple pour creér un objet XML::Reader avec un fichier:



  my $rdr = XML::Reader->new(input.xml);



Voici un autre exemple pour creér un objet XML::Reader avec une re\k:'|\n:urence a\k:` une chai\k:^ne de caracte\k:`res:



  my $rdr = XML::Reader->new(\<data>abc</data>);



Voici un exemple pour creér un objet XML::Reader avec une re\k:'|\n:urence a\k:` un fichier:



  open my $fh, <, input.xml or die "Error: $!";
  my $rdr = XML::Reader->new($fh);



Voici un exemple pour creér un objet XML::Reader avec \*STDIN:



  my $rdr = XML::Reader->new(\*STDIN);



On peut ajouter une ou plusieurs options dans une re\k:'|\n:urence a\k:` un hashage:
option {parse_ct => } Option {parse_ct => 1} permet de lire les commentaires, le defaut est {parse_ct => 0}
option {parse_pi => } Option {parse_pi => 1} permet de lire les processing-instructions et les XML-declarations, le de\k:'est {parse_pi => 0}
option {using => } Option {using => } permet de se\k:'un arbre pre\k:'dans le XML.

La syntaxe est {using => [’/path1/path2/path3’, ’/path4/path5/path6’]}

option {filter => } et {mode => } Option {filter => 2} ou {mode => ’attr-bef-start’} affiche tous les e\k:'|\n:uments, y compris les attributs.

Option {filter => 3} ou {mode => ’attr-in-hash’} supprime les attributs (c’est a\k:` dire il supprime toutes les lignes qui sont $rdr->type eq ’@’). En revanche, le contenu des attributs sont retourne\k:'dans le hashage $rdr->att_hash.

Option {filter => 4} ou {mode => ’pyx’} creé une ligne individuel pour chaque balise de de\k:'de fin, pour chaque attribut, pour chaque commentaire et pour chaque processing-instruction. Ce fonctionnement permet, en effet, de ge\k:'|\n:urer un format PYX.

Option {filter => 5} ou {mode => ’branches’} selectionne uniquement les donneés pour les racines (root). Les e\k:'|\n:uments pour chaque root sont accumule\k:'dans une re\k:'|\n:urence a\k:` un array (comme specifie\k:'dans la partie branch) et quand le branch est complet, les donneés sont retourne\k:'Cette proce\k:'se trouve au milieu entre l’option using (qui retourne les e\k:'|\n:uments un par un) et la fonction slurp_xml (qui accumule les donneés dans un branch, et tous les branches sont accumule\k:'dans une seule structure en me\k:'\h |\n:umoire).

La syntaxe est {filter => 2|3|4|5, mode => ’attr-bef-start’|’attr-in-hash’|’pyx’|’branches’}, le de\k:'est {filter => 2, mode => ’attr-bef-start’}

option {strip => } Option {strip => 1} supprime les caracte\k:`res blancs au de\k:'et a\k:` la fin d’un texte ou d’un commentaire. (les caracte\k:`res blancs d’un attribut ne sont jamais supprime\k:'L’option {strip => 0} laisse le texte ou commentaire intacte.

La syntaxe est {strip => 0|1}, le de\k:'est {strip => 1}

option {dupatt => } L’option {dupatt => ’*’} permet de lire plusieurs attributs avec le me\k:^me nom. Les diffe\k:'valeurs sont donc concateneés par ’*’.

    Me\k:'\h |\n:uthodes

Un objet du type XML::Reader a des me\k:'suivantes:
iterate La me\k:'iterate lit un e\k:'|\n:ument XML. Elle retourne 1, si la lecture a e\k:'|\n:u un succe\k:`s, ou undef a\k:` la fin du fichier XML.
path La me\k:'path retourne le chemin complet de la ligne en cours, les attributs sont re\k:'|\n:usente\k:'avec des caracte\k:`res ’@’.
value La me\k:'value retourne la valeur de la ligne en cours (c’est a\k:` dire le texte ou l’attribut).

Conseil: en cas de {filter => 2 ou 3} avec une de\k:'(c’est a\k:` dire $rdr->is_decl == 1), il vaut mieux ne pas prendre compte de la valeur (elle sera vide, de toute fac\k:,on). Un bout de programme:



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



Le programme ci-dessus ne s’applique *pas* a\k:` {filter => 4}, dans ce cas un simple print $rdr->value; est suffisant:



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



comment La me\k:'comment retourne le commentaire d’un fichier XML. Il est conseille\k:'de tester $rdr->is_comment avant d’acce\k:'a\k:` la me\k:'comment.
type La me\k:'type retourne ’T’ quand il y a du texte dans le XML, et ’@’ quand il y a un attribut.

Si l’option {filter => 4} est active, les possibilite\k:'sont: ’T’ pour du texte, ’@’ poir un attribut, ’S’ pour une balise de de\k:'’E’ pour une balise de fin, ’#’ pour un commentaire, ’D’ pour une de\k:'’?’ pour une processing-instruction.

tag La me\k:'tag retourne le nom de la balise en cours.
attr La me\k:'attr retourne le nom de l’attribut en cours (elle retourne une chai\k:^ne de caracte\k:`res vide si l’e\k:'|\n:ument en cours n’est pas un attribut)
level La me\k:'level retourne le niveau de cascadage (un nombre > 0)
prefix La me\k:'prefix retourne le pre\k:'du chemin (c’est la partie du chemin qui a e\k:'|\n:u supprime\k:'dans l’option {using => ...}). Elle retourne une chai\k:^ne de caracte\k:`res vide au cas ou\k:` l’option {using => ...} n’a pas e\k:'|\n:u specifieé.
att_hash La me\k:'att_hash retourne une re\k:'|\n:urence a\k:` l’hachage des attributs de la balise de de\k:'en cours (au cas ou\k:` l’e\k:'|\n:ument en cours n’est pas une balise de de\k:'un hachage vide est retourne\k:'\h |\n:u)
dec_hash La me\k:'dec_hash retourne une re\k:'|\n:urence a\k:` l’hachage des attributs de la XML-declaration en cours (au cas ou\k:` l’e\k:'|\n:ument en cours n’est pas une XML-declaration, un hachage vide est retourne\k:'\h |\n:u)
proc_tgt La me\k:'proc_tgt retourne la partie cible (c’est a\k:` dire la premie\k:`re partie) de la processing-instruction (au cas ou\k:` l’e\k:'|\n:ument en cours n’est pas une processing-instruction, une chai\k:^ne de caracte\k:`res vide est retourne\k:'\h |\n:u)
proc_data La me\k:'proc_data retourne la partie donneé (c’est a\k:` dire la deuxie\k:`me partie) de la processing-instruction (au cas ou\k:` l’e\k:'|\n:ument en cours n’est pas une processing-instruction, une chai\k:^ne de caracte\k:`res vide est retourne\k:'\h |\n:u)
pyx La me\k:'pyx retourne la chai\k:^ne de caracte\k:`res format PYX de l’e\k:'|\n:ument XML en cours.

La chai\k:^ne de caracte\k:`res format PYX est une chai\k:^ne de caracte\k:`res avec un premier caracte\k:`re spe\k:'Ce premier caracte\k:`re de chaque ligne PYX de\k:'le type: si le premier caracte\k:`re est un ’(’, alors c\k:,a signifie une balise de de\k:'Si le premier caracte\k:`re est un ’)’, alors c\k:,a signifie une balise de fin. Si le premier caracte\k:`re est un ’A’, alors c\k:,a signifie un attribut. Si le premier caracte\k:`re est un ’-’, alors c\k:,a signifie un texte. Si le premier caracte\k:`re est un ’?’, alors c\k:,a signifie une processing-instruction. (voir <http://www.xml.com/pub/a/2000/03/15/feature/index.html> pour une introduction a\k:` PYX)

La me\k:'pyx n’est utile que pour l’option {filter => 4}, sinon, pour un {filter => } diffe\k:'de 4, on retourne undef.

is_start La me\k:'is_start retourne 1, si l’e\k:'|\n:ument en cours est une balise de de\k:'sinon 0 est retourne\k:'\h |\n:u.
is_end La me\k:'is_end retourne 1, si l’e\k:'|\n:ument en cours est une balise de fin, sinon 0 est retourne\k:'\h |\n:u.
is_decl La me\k:'is_decl retourne 1, si l’e\k:'|\n:ument en cours est une XML-declaration, sinon 0 est retourne\k:'\h |\n:u.
is_proc La me\k:'is_proc retourne 1, si l’e\k:'|\n:ument en cours est une processing-instruction, sinon 0 est retourne\k:'\h |\n:u.
is_comment La me\k:'is_comment retourne 1, si l’e\k:'|\n:ument en cours est un commentaire, sinon 0 est retourne\k:'\h |\n:u.
is_text La me\k:'is_text retourne 1, si l’e\k:'|\n:ument en cours est un texte, sinon 0 est retourne\k:'\h |\n:u.
is_attr La me\k:'is_attr retourne 1, si l’e\k:'|\n:ument en cours est un attribut, sinon 0 est retourne\k:'\h |\n:u.
is_value La me\k:'is_attr retourne 1, si l’e\k:'|\n:ument en cours est un texte ou un attribut, sinon 0 est retourne\k:'Cette me\k:'est pluto\k:^t utile pour l’option {filter => 4, mode => ’pyx’}, ou\k:` on peut tester l’utilite\k:'de la me\k:'value.
rx C’est l’indexe de la branche en cours (cette fonction est valide uniquement avec {filter => 5}).
rvalue C’est une re\k:'|\n:urence a\k:` une valeur scalaire (ou a\k:` une liste) de la branche en cours (cette fonction est valide uniquement avec {filter => 5}). rvalue est plus rapide, mais moins simple a\k:` utiliser que la fonction value (avec rvalue vous devez faire votre de\k:'|\n:ufe\k:'vous me\k:^me).
rstem Cette fonction est identique a\k:` la fonction path

OPTION USING

L’option {using => ...} permet de se\k:'un sous-arbre du XML.

Voici comment c\k:,a fonctionne en de\k:'\h |\n:utail...

L’option {using => [’/chemin1/chemin2/chemin3’, ’/chemin4/chemin5/chemin6’]} d’abord elimine toutes les lignes ou\k:` le chemin ne commence pas avec ’/chemin1/chemin2/chemin3’ ou ’/chemin4/chemin5/chemin6’.

Les lignes restantes (ceux qui n’ont pas e\k:'|\n:u elimine\k:'ont un chemin plus court. En fait le pre\k:'\h |\n:ufixe ’/chemin1/chemin2/chemin3’ (ou ’/chemin4/chemin5/chemin6’) a e\k:'|\n:u supprime\k:'En revanche, ce pre\k:'supprime\k:'\h |\n:u apparai\k:^t dans la me\k:'prefix.

On dit que ’/chemin1/chemin2/chemin3’ (ou ’/chemin4/chemin5/chemin6’) sont absolu et comple\k:`t. Le mot absolu signifie que chaque chemin commence forcement par un caracte\k:`re ’/’, et le mot comple\k:`t signifie que la dernie\k:`re partie ’chemin3’ (ou ’chemin6’) sera suivi implicitement par un caracte\k:`re ’/’.

    Exemple avec using

Le programme suivant prend un fichier XML et le parse avec XML::Reader, y compris l’option ’using’ pour cibler des e\k:'|\n:uments spe\k:'\h |\n:ucifiques:



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



Voici le re\k:'de ce programme:



   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



    Exemple sans using

Le programme suivant prend un fichier XML et le parse avec XML::Reader, mais sans 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;
  }



Comme on peut constater dans le re\k:'suivant, il y a beaucoup plus de lignes, le pre\k:'est vide et le chemin est beaucoup plus longue par rapport au programme pre\k:'|\n:udent:



   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

L’option {parse_ct => 1} permet de parser les commentaires (normalement, les commentaires ne sont pas pris en compte par XML::Reader, le de\k:'est {parse_ct => 0}.

Voici un exemple ou\k:` les commentaires ne sont pas pris en compte par de\k:'\h |\n:ufaut:



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



Voici le re\k:'\h |\n:usultat:



  Text xyz stu test



Ensuite, les me\k:^mes donneés XML et le me\k:^me algorithme, sauf l’option {parse_ct => 1}, qui est maintenant active:



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



Voici le re\k:'\h |\n:usultat:



  Text xyz
  Found comment   remark
  Text stu test



OPTION PARSE_PI

L’option {parse_pi => 1} permet de parser les processing-instructions et les XML-Declarations (normalement, ni les processing-instructions, ni les XML-Declarations ne sont pris en compte par XML::Reader, le de\k:'\h |\n:ufaut est {parse_pi => 0}.

Comme exemple, on prend exactement les me\k:^mes donneés XML et le me\k:^me algorithme du paragraphe pre\k:'|\n:udent, sauf l’option {parse_pi => 1}, qui est maintenant active (avec l’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;
  }



Notez le unless $rdr->is_decl dans le programme ci-dessus. C’est pour e\k:'le texte vide apre\k:`s la XML-de\k:'\h |\n:uclaration.

Voici le re\k:'\h |\n:usultat:



  Found decl      version=1.0
  Text xyz
  Found comment   remark
  Text stu
  Found proc      t=ab, d=cde
  Text test



OPTION FILTER / MODE

L’option {filter => } ou {mode => } permet de se\k:'des diffe\k:'modes d’ope\k:'pour le traitement du XML.

    Option filter 2 mode attr-bef-start

Avec l’option {filter => 2} ou {mode => ’attr-bef-start’}, XML::Reader ge\k:'une ligne pour chaque morceau de texte. Si la balise pre\k:'|\n:udente est une balise de de\k:'alors la me\k:'is_start retourne 1. Si la balise suivante est une balise de fin, alors la me\k:'is_end retourne 1. Si la balise pre\k:'|\n:udente est une balise de commentaire, alors la me\k:'\h |\n:uthode is_comment retourne 1. Si la balise pre\k:'|\n:udente est une balise de XML-declaration, alors la me\k:'is_decl retourne 1. Si la balise pre\k:'|\n:udente est une balise de processing-instruction, alors la me\k:'is_decl retourne 1.

De plus, les attributs sont repre\k:'|\n:us par des lignes supple\k:'avec la syntaxe ’/@...’.

Option {filter => 2, mode => ’attr-bef-start’} est le de\k:'\h |\n:ufaut.

Voici un exemple...



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

  # les quatres alternatives ci-dessous sont equivalent:
  # ----------------------------------------------------
  #   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;
  }



Le programme (avec l’option {filter => 2, mode => ’attr-bef-start’} implicitement par de\k:'ge\k:'le re\k:'suivant:



  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



L’option (implicite) {filter => 2, mode => ’attr-bef-start’} permet e\k:'de reconstruire la structure du XML avec l’assistance des me\k:'is_start and is_end. Notez que dans le re\k:'ci-dessus, la premie\k:`re ligne (Path: /root, Value:) est vide, mais elle est importante pour la structure du XML.

Prenons-nous le me\k:^me exemple {filter => 2, mode => ’attr-bef-start’} avec un algorithme pour reconstruire la structure originale du XML. En plus, on exige de rajouter des caracte\k:`res ** ** pour chaque texte (mais pas les balises et pas non plus les attributs):



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

  # les quatres alternatives ci-dessous sont equivalent:
  # ----------------------------------------------------
  #   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";
      }
  }



...voici le re\k:'\h |\n:usultat:



  <root>
    <test param=<>v">
      <a>
        <b>
          ** "e" **
          <data id=<>z'>
            ** g&<> **
          </data>
          ** f **
        </b>
      </a>
    </test>
    ** x yz **
  </root>



...ce qui donne preuve que la structure originale du XML n’est pas perdu.

    Option filter 3 mode attr-in-hash

Pour la plupart, l’option {filter => 3, mode => ’attr-in-hash’} fonctionne comme l’option {filter => 2, mode => ’attr-bef-start’}.

Mais il y a une diffe\k:'avec l’option {filter => 3, mode => ’attr-in-hash’}, les attributs sont supprimeés et a\k:` la place, les attributs sont pre\k:'|\n:us dans un hashage "$rdr->att_hash()" pour chaque balise de de\k:'\h |\n:ubut.

Ainsi, dans l’algorithme pre\k:'|\n:udent, on peut supprimer la variable globale %at et la remplacer par %{$rdr->att_hash}:

Voici un nouveau algorithme avec {filter => 3, mode => ’attr-in-hash’}, on ne s’occupe pas des attributs (c’est-a\k:`-dire, on ne verifie pas $rdr->type eq ’@’) et (comme dXja\k:` mentionne\k:'ci-dessus) la variable %at est remplaceé par %{$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});

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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";
      }
  }



...le re\k:'de {filter => 3, mode => ’attr-in-hash’} est identique au re\k:'de {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>



Finalement on pourrait (et on devrait) effectuer l’e\k:'XML par un autre module. Je propose d’utiliser le module XML::MinWriter. Voici un programme qui utilise XML::MinWriter pour e\k:'du XML:



  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();



Voici le re\k:'de XML::MinWriter:



  <root
  ><test param="<>v""
  ><a
  ><b
  >** "e" **<data id="<>z"
  >** g&<> **</data
  >** f **</b
  ></a
  ></test
  >** x yz **</root
  >



A la premie\k:`re lecture, le format est bizarre, mais c’est du XML tout a\k:` fait correct.

    Option filter 4 mode pyx

Me\k:^me si ce n’est pas la raison principale de XML::Reader, l’option {filter => 4, mode => ’pyx’} permet de ge\k:'|\n:urer des lignes individuelles pour chaque balise de de\k:'de fin, commentaires, processing-instruction et XML-Declaration. Le but est de ge\k:'|\n:urer une chai\k:^ne de caracte\k:`res du mode\k:`le PYX pour l’analyse par la suite.

Voici un exemple:



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

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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;
  }



et voici le re\k:'\h |\n:usultat:



  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



Il faut dire que les commentaires, qui sont ge\k:'|\n:ure\k:'avec l’option {parse_ct => 1}, ne font pas partie du standard PYX. En fait, les commentaires sont ge\k:'|\n:ure\k:'avec un caracte\k:`re ’#’ qui n’existe pas dans le standard. Voici un exemple:



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

  my $text = q{
    <delta>
      <!-- remark -->
    </delta>};

  my $rdr = XML::Reader->new(\$text, {filter => 4, parse_ct => 1});

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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;
  }



Voici le re\k:'\h |\n:usultat:



  Type = S, pyx = (delta
  Type = #, pyx = #remark
  Type = E, pyx = )delta



Avec l’option {filter => 4, mode => ’pyx’}, les me\k:'habituelles restent accessibles: 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 or att_hash. Voici un exemple:



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

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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"; }
  }



Voici le re\k:'\h |\n:usultat:



  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



Notez que v=1 (c’est a\k:` dire $rdr->is_value == 1) pour tous les textes et pour tous les attributs.

    Option filter 5 mode branches

Avec option {filter => 5, mode => ’branches’}, on spe\k:'une (ou plusieurs) racines (roots), chaque racine a une collection de branches. En re\k:'on obtient un enregistrement pour chaque root dans l’aborescence XML.

Une racine peut commencer par un slash simple (par ex. {root => ’/tag1/tag2’}), dans ce cas la racine est absolue. Ou la racine peut commencer par un double-slash (par ex. {root => ’//tag1/tag2’}), dans ce cas la racine est relative. Si la racine ne commence pas par un slash, dans ce cas la racine est e\k:'\h |\n:ugalement relative.

Chaque enregistrement contient les e\k:'|\n:uments qui ont e\k:'|\n:u spe\k:'|\n:u dans les branches. (il y a un cas particulier: quand la branche est ’*’, dans ce cas l’aborescence du sous-arbre est construit)

Pour obtenir des valeurs qui sont spe\k:'dans les branches, on peut utiliser la fonction $rdr->rvalue.

Voici un exemple:



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



On veut lire les e\k:'|\n:uments name, street et city de tous les clients dans le chemin relatif (’customer’) et on veut e\k:'lire les fournisseurs dans le chemin absolu (’/data/supplier’). Puis, on veut lire les clients dans le chemin relatif (’//customer’) en format sous-arborescence XML (voir {branch => ’*’}). Finalement on veut lire le chemin relatif (’p’) en format sous-arborescence XML.

Les donneés pour la premie\k:`re racine (’customer’) sont identifiable par $rdr->rx == 0, les donneés pour la deuxie\k:`me racine (’/data/supplier’) sont identifiable par $rdr->rx == 1, les donneés pour la troisie\k:`me racine (’//customer’) sont identifiable par $rdr->rx == 2, les donneés pour la quatrie\k:`me racine (’p’) sont identifiable par $rdr->rx == 3 et les donneés pour la cinquie\k:`me racine (’//customer’) sont identifiable par $rdr->rx == 4.

Dans l’exemple ci-dessous, le parame\k:`tre {branch => ’*’} indique que le re\k:'sera du XML et le parame\k:`tre {branch => ’+’} indique que le re\k:'sera une liste des instructions PYX.

Dans le programme ci-dessus on va utiliser la fonction $rdr->rvalue pour lire les donnees:



  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 => + },
  );

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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";



Voici le re\k:'\h |\n:usultat:



  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



Nous pouvons e\k:'utiliser la fonction $rdr->value pour obtenir les me\k:^mes donneés:



  my $rdr = XML::Reader->new(\$line2, {filter => 5},
    { root => customer,       branch => [@name, street, city] },
    { root => p,              branch => * },
  );

  # les trois alternatives ci-dessous sont equivalent:
  # --------------------------------------------------
  #   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";



Voici le re\k:'\h |\n:usultat:



  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>



Il faut dire ici que le re\k:'root3 / output1 { root => ’p’, branch => ’*’ } contient toujours le sous-arbre XML le plus important. Ou dit autrement: la sortie P: <p>b1</p> et P: <p>b2</p> n’est pas possible, car une ligne plus importante qui contient les lignes P: <p>b1</p> et P: <p>b2</p> exsiste de\k:'cette ligne est P: <p><p>b1</p><p>b2</p></p>.

OPTION DUPATT

L’option dupatt permet de lire multiples attributs avec le me\k:^me nom dans une seule valeur. Le principe fonctionne par la concatenation des diffe\k:'valeurs dans une seule variable, se\k:'|\n:u par un caracte\k:`re spe\k:'La liste des attributs est ensuite trie\k:'par order alphabe\k:'\h |\n:utique.

L’option dupatt est valable uniquement si XML::Reader a e\k:'|\n:u utilise\k:'avec l’option XML::Parsepp.

Par exemple, le code suivant concate\k:`ne les attribut doublons avec le caracte\k:`re |: (La chai\k:^ne de caracte\k:`res ($str) {dupatt => $str} est limite\k:'a\k:` des caracte\k:`res Ascii visibles, sauf les caracte\k:`res alpha-nume\k:'’ ou ")



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



Voici le re\k:'du programme:



  Path: /data              , Value:
  Path: /data/item/@a1     , Value: def
  Path: /data/item/@a2     , Value: abc|ghi
  Path: /data/item         , Value:
  Path: /data              , Value:



EXEMPLES

Examinons-nous le XML suivant, ou\k:` nous souhaitons extraire les valeurs dans la balise <item> (c’est la premie\k:`re partie ’start...’, et non pas la partie ’end...’ qui nous inte\k:'ensuite les attributs p1 et p3. La balise <item> doit e\k:^tre dans le chemin ’/start/param/data (et non pas dans le chemin /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>};



Nous expectons exactement 4 lignes de sortie dans le re\k:'(c’est a\k:` dire la ligne ’dataz’ / ’start9’ ne fait pas partie du re\k:'\h |\n:usultat):



  item = start1, p1 = a, p3 = c
  item = start2, p1 = d, p3 = f
  item = start3, p1 = g, p3 = i
  item = start4, p1 = m, p3 = o



    Exemple filter 2 mode attr-bef-start

Ci-dessous un programme pour parser le XML avec l’option {filter => 2, mode => ’attr-bef-start’}. (Notez que le pre\k:'\h |\n:ufixe ’/start/param/data/item’ est renseigne\k:'dans l’option {using =>} de la fonction new). En plus, nous avons besoins de 2 variables scalaires ’$p1’ et ’$p3’ pour enregistrer les parame\k:`tres ’/@p1’ et ’/@p3’ et les transfe\k:'dans la partie ’$rdr->is_start’ du programme, ou\k:` on peut les afficher.



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



    Exemple filter 3 mode attr-in-hash

Avec l’option {filter => 3, mode => ’attr-in-hash’}, nous pouvons annuler les deux variables ’$p1’ et ’$p3’. Le programme devient assez simple:



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



    Exemple filter 4 mode pyx

Avec l’option {filter => 4, mode => ’pyx’}, par contre, le programme devient plus complique\k:'Comme de\k:'\h |\n:uja\k:` montre\k:'dans l’exemple {filter => 2, mode => ’attr-bef-start’}, nous avons besoin de deux variables scalaires (’$p1’ et ’$p3’) pour enregistrer les parame\k:`tres ’/@p1’ et ’/@p3’ et les transfe\k:'a\k:` l’endoit ou\k:` on peut les afficher. En plus, nous avons besoin de compter les valeurs de texte (voir variable ’$count’ ci-dessous), afin d’identifier la premie\k:`re partie du texte ’start...’ (ce que nous voulons afficher) et supprimer la deuxie\k:`me partie du texte ’end...’ (ce que nous ne voulons pas afficher).



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



    Exemple filter 5 mode branches

On peut combiner {filter => 5, mode => ’branches’} avec des expressions re\k:'pour parser les donneés XML:



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



    Exemple condition d’attribut

Ci-dessous un autre document XML ($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>
  };



En utilisant des chemins (root => ... and branch => ...) on peut rajouter des conditions d’attribut (’/path1[@attr=val]/...’), par ex.:



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



Voici le re\k:'\h |\n:usultat:



  smith = rio|ny    , gates = dallas
  smith = undef       , gates = cambridge
  smith = big apple , gates = undef



FONCTIONS

    Fonction slurp_xml

La fonction slurp_xml lit un fichier XML et aspire son contenu dans une re\k:'|\n:urence a\k:` une liste. Voici un exemple ou\k:` nous souhaitons aspirer le nom, la rue et la ville de tous les clients dans le chemin ’/data/order/database/customer’. Nous souhaitons aussi d’aspirer le ’supplier’ dans le chemin ’/data/supplier’.



  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", $_;
  }



Le premier parame\k:`tre de slurp_xml est ou le nom du fichier (ou une une re\k:'|\n:urence a\k:` un scalaire, ou une re\k:'|\n:urence a\k:` un fichier ouvert) du XML qu’on veut aspirer. Dans notre cas nous avons une re\k:'|\n:urence a\k:` un scalaire \$line2. Le parame\k:`tre suivant est la racine de l’arbre qu’on veut aspirer (dans notre cas c’est ’/data/order/database/customer’) et nous donnons une liste des e\k:'|\n:uments que nous souhaitons se\k:'relative a\k:` la racine. Dans notre cas c’est [’@name’, ’street’, ’city’]. Le parame\k:`tre suivant est la deuxie\k:`me racine (de\k:'root/branch), dans ce cas c’est root => ’/data/supplier’ avec branch => [’/’].

Voici le re\k:'\h |\n:usultat:



  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>



Le fonctionnement de slurp_xml est similaire a\k:` XML::Simple, c’est a\k:` dire il lit toutes les donneés dans un seul coup dans une structure en me\k:'En revanche, la diffe\k:'\h |\n:urence est que slurp_xml permet de spe\k:'les donneés qu’on veut avant de faire l’aspiration, ce qui re\k:'dans une structure en me\k:'souvent plus petite et moins compliqueé.

On peut utiliser slurp_xml() avec des attributs doublons. Dans ce cas il faut faire deux choses: Premie\k:`rement il faut faire un use XML::Reader avec qw(XML::Parsepp slurp_xml). Deuxie\k:`mement il faut faire appel a\k:` slurp_xml avec l’option { dupatt => ’|’ }, comme dans l’exemple ci-dessous:



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



Voici le re\k:'\h |\n:usultat:



  <data atr1=abc|ghi atr2=def></data>



AUTEUR

Klaus Eichner, Mars 2009

COPYRIGHT ET LICENSE

Voici le texte original en Anglais:

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

MODULES ASSOCIES

Si vous souhaitez e\k:'du XML, je propose d’utiliser le module XML::Writer ou XML::MinWriter. Chacun de ces deux modules se pre\k:'avec une interface simple pour e\k:'un fichier XML. Si vous ne me\k:'pas le texte et les balises (ce qu’on appelle en Anglais non-mixed content XML), je propose de mettre les options DATA_MODE=>1 et DATA_INDENT=>2, ainsi votre re\k:'sera proprement formate\k:'selon les re\k:`gles XML.

REFERENCES

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_FR (3) 2014-12-28

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