|
NAMEXML::Reader_fr - Lire du XML avec des informations du chemin, conduit par un parseur d'extraction.TRADUCTIONThis 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.pmCe document est une traduction Française de l'Anglais du module XML::Reader. Pour obtenir la source Perl du module, consultez le fichier XML/Reader.pm SYNOPSISuse XML::Reader qw(XML::Parser); my $text = q{<init>n <?test pi?> t<page node="400">m <!-- remark --> r</page></init>}; my $rdr = XML::Reader->new(\$text); while ($rdr->iterate) { printf "Path: %-19s, Value: %s\n", $rdr->path, $rdr->value; } Ce programme crée le résultat 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 vérifier que l'appel a réussi: my $rdr = eval{ XML::Reader->new('test.xml') } or warn "Can't XML::Reader->new() because $@"; if ($rdr) { # ... do something with $rdr ... } else { # ... do some error handling ... } UTILISATIONNormalement, on n'utilise pas XML::Reader tout seul, on utilise plutô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 procédure pour choisir entre XML::Parser et XML::Parsepp: XML::Reader utilise XML::Parser comme sous-module. Cette configuration marche très bien, sauf dans le cas où 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 dépend des deux modules XML::Parser et XML::Parsepp. En conséquence, si vous ne pouvez pas installer XML::Parser, vous pouvez quand même installer XML::Reader, mais vous ne lancez pas les tests. DESCRIPTIONXML::Reader est un module simple et facile à utiliser pour parser des fichiers XML de manière séquentielle (aussi appellé parseur guidé par l'extraction) et, en même temps, il enregistre le chemin complet du XML.Il a été développé comme une couche sur XML::Parser/XML::Parsepp (quelques fonctionalités basiques ont été copié de XML::TokeParser). XML::Parser, XML::Parsepp et XML::TokeParser utilisent chacun une méthode d'extraction séquentielle, mais ils n'enregistrent pas le chemin du XML. De plus, avec les interfaces de XML::Parser, XML::Parsepp et XML::TokeParser, on est obligé de séparer les balises de début, les balises de fin et du texte, ce qui, à mon avis, rend l'utilisation assez compliqué. (par contre, si on le souhaite, XML::Reader peut agir d'une manière à ce que les balises de début, les balises de fin et du texte sont séparés, par l'option {filter => 4, mode => 'pyx'}). Il y a aussi XML::TiePYX, qui permet de parser des fichiers XML de manière séquentielle (voir <http://www.xml.com/pub/a/2000/03/15/feature/index.html> pour consulter une introduction à PYX). Mais même avec XML::TiePYX, il faut séparer les balises de début, 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 début, les balises de fin et le texte sont traduits en expressions similaires à XPath. En conséquence, il est inutile de compter des balises individuelles, on a un chemin et une valeur, et ça suffit. (par contre, au cas où on veut opérer XML::Reader en fonctionnement compatible à PYX, il y a toujours l'option {filter => 4, mode => 'pyx'}, comme déjà mentionné 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 être parsé avec XML::Reader en utilisant les méthodes "iterate" pour lire séquentiellement les données XML, et en utilisant les méthodes "path" et "value" pour extraire le chemin et la valeur à un endroit précis dans le fichier XML. Si nécessaire, on peut également identifier les balises individuelles de début et de fin: Il y a une méthode "is_start", qui donne 1 ou 0 (c'est à dire: 1, s'il y a une balise de début à la position actuelle, sinon 0). Il y a également la méthode équivalente "is_end". En plus, il y a les méthodes "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 résultat: 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 INTERFACECréation objetPour créer un objet, on utilise la syntaxe suivante:my $rdr = XML::Reader->new($data, {strip => 1, filter => 2, using => ['/path1', '/path2']}); L'élément $data est obligatoire, il est le nom d'un fichier XML, ou une URL qui commence par 'http://...', ou la référence à une chaîne de caractères, dans ce cas le contenu de cette chaîne de caractères est accepté comme XML. Sinon, $data peut également être une référence à un fichier, comme par exemple \*STDIN. Dans ce cas, la référence de fichier est utiliser pour lire le XML. Voici un exemple pour créer un objet XML::Reader avec un fichier: my $rdr = XML::Reader->new('input.xml'); Voici un autre exemple pour créer un objet XML::Reader avec une référence à une chaîne de caractères: my $rdr = XML::Reader->new(\'<data>abc</data>'); Voici un exemple pour créer un objet XML::Reader avec une référence à un fichier: open my $fh, '<', 'input.xml' or die "Error: $!"; my $rdr = XML::Reader->new($fh); Voici un exemple pour créer un objet XML::Reader avec \*STDIN: my $rdr = XML::Reader->new(\*STDIN); On peut ajouter une ou plusieurs options dans une référence à un hashage:
MéthodesUn objet du type XML::Reader a des méthodes suivantes:
OPTION USINGL'option {using => ...} permet de sélectionner un sous-arbre du XML.Voici comment ça fonctionne en détail... L'option {using => ['/chemin1/chemin2/chemin3', '/chemin4/chemin5/chemin6']} d'abord elimine toutes les lignes où le chemin ne commence pas avec '/chemin1/chemin2/chemin3' ou '/chemin4/chemin5/chemin6'. Les lignes restantes (ceux qui n'ont pas été eliminé) ont un chemin plus court. En fait le préfixe '/chemin1/chemin2/chemin3' (ou '/chemin4/chemin5/chemin6') a été supprimé. En revanche, ce préfixe supprimé apparaît dans la méthode "prefix". On dit que '/chemin1/chemin2/chemin3' (ou '/chemin4/chemin5/chemin6') sont "absolu" et "complèt". Le mot "absolu" signifie que chaque chemin commence forcement par un caractère '/', et le mot "complèt" signifie que la dernière partie 'chemin3' (ou 'chemin6') sera suivi implicitement par un caractère '/'. Exemple avec usingLe programme suivant prend un fichier XML et le parse avec XML::Reader, y compris l'option 'using' pour cibler des éléments spécifiques: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 résultat 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 usingLe 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 résultat suivant, il y a beaucoup plus de lignes, le préfixe est vide et le chemin est beaucoup plus longue par rapport au programme précédent: 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_CTL'option {parse_ct => 1} permet de parser les commentaires (normalement, les commentaires ne sont pas pris en compte par XML::Reader, le défaut est {parse_ct => 0}.Voici un exemple où les commentaires ne sont pas pris en compte par défaut: 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 résultat: Text 'xyz stu test' Ensuite, les mêmes données XML et le mê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 résultat: Text 'xyz' Found comment remark Text 'stu test' OPTION PARSE_PIL'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 défaut est {parse_pi => 0}.Comme exemple, on prend exactement les mêmes données XML et le même algorithme du paragraphe précédent, 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 éviter le texte vide après la XML-déclaration. Voici le résultat: Found decl version='1.0' Text 'xyz' Found comment remark Text 'stu' Found proc t=ab, d=cde Text 'test' OPTION FILTER / MODEL'option {filter => } ou {mode => } permet de sélectionner des différents modes d'opératoires pour le traitement du XML.Option filter 2 mode attr-bef-startAvec l'option {filter => 2} ou {mode => 'attr-bef-start'}, XML::Reader génère une ligne pour chaque morceau de texte. Si la balise précédente est une balise de début, alors la métode "is_start" retourne 1. Si la balise suivante est une balise de fin, alors la métode "is_end" retourne 1. Si la balise précédente est une balise de commentaire, alors la méthode "is_comment" retourne 1. Si la balise précédente est une balise de XML-declaration, alors la méthode "is_decl" retourne 1. Si la balise précédente est une balise de processing-instruction, alors la méthode "is_decl" retourne 1.De plus, les attributs sont représentés par des lignes supplémentaires avec la syntaxe '/@...'. Option {filter => 2, mode => 'attr-bef-start'} est le défaut. 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 défaut) génère le résultat 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 également de reconstruire la structure du XML avec l'assistance des méthodes "is_start" and "is_end". Notez que dans le résultat ci-dessus, la première ligne ("Path: /root, Value:") est vide, mais elle est importante pour la structure du XML. Prenons-nous le mê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 caractè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 résultat: <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-hashPour 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 différence: avec l'option {filter => 3, mode => 'attr-in-hash'}, les attributs sont supprimées et à la place, les attributs sont présentés dans un hashage "$rdr->"att_hash"()" pour chaque balise de début. Ainsi, dans l'algorithme précédent, 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-à-dire, on ne verifie pas $rdr->type eq '@') et (comme d´jà mentionné ci-dessus) la variable %at est remplacée 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 résultat de {filter => 3, mode => 'attr-in-hash'} est identique au résultat 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'écriture XML par un autre module. Je propose d'utiliser le module XML::MinWriter. Voici un programme qui utilise XML::MinWriter pour écrire 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 résultat de XML::MinWriter: <root ><test param="<>v"" ><a ><b >** "e" **<data id="<>z'" >** 'g'&<> **</data >** f **</b ></a ></test >** x yz **</root > A la première lecture, le format est bizarre, mais c'est du XML tout à fait correct. Option filter 4 mode pyxMême si ce n'est pas la raison principale de XML::Reader, l'option {filter => 4, mode => 'pyx'} permet de générer des lignes individuelles pour chaque balise de début, de fin, commentaires, processing-instruction et XML-Declaration. Le but est de générer une chaîne de caractères du modè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 résultat: 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 générés avec l'option {parse_ct => 1}, ne font pas partie du standard "PYX". En fait, les commentaires sont générés avec un caractè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 résultat: Type = S, pyx = (delta Type = #, pyx = #remark Type = E, pyx = )delta Avec l'option {filter => 4, mode => 'pyx'}, les méthodes 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 résultat: 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 à dire $rdr->"is_value" == 1) pour tous les textes et pour tous les attributs. Option filter 5 mode branchesAvec option {filter => 5, mode => 'branches'}, on spécifie une (ou plusieurs) racines ("roots"), chaque racine a une collection de "branches". En résultat 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 également relative. Chaque enregistrement contient les éléments qui ont été spécifié 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 spécifiées 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="o'rob" id="444"> <street>pod alley</street> <city>no city</city> </customer> <customer1 name="troy" id="333"> <street>one way</street> <city>any city</city> </customer1> <tcustomer name="nbc" id="777"> <street>away</street> <city>acity</city> </tcustomer> <supplier>hhh</supplier> <zzz> <customer name='"sue"' id="111"> <street>baker street</street> <city>sidney</city> </customer> </zzz> <order> <database> <customer name="<smith>" id="652"> <street>high street</street> <city>boston</city> </customer> <customer name="&jones" id="184"> <street>maple street</street> <city>new york</city> </customer> <customer name="stewart" id="520"> <street> ring road </street> <city> "'&<A>'" </city> </customer> </database> </order> <dummy value="ttt">test</dummy> <supplier>iii</supplier> <supplier>jjj</supplier> <p> <p>b1</p> <p>b2</p> </p> <p> b3 </p> </data> }; On veut lire les éléments "name", "street" et "city" de tous les clients dans le chemin relatif ('customer') et on veut également 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 données pour la première racine ('customer') sont identifiable par $rdr->rx == 0, les données pour la deuxième racine ('/data/supplier') sont identifiable par $rdr->rx == 1, les données pour la troisième racine ('//customer') sont identifiable par $rdr->rx == 2, les données pour la quatrième racine ('p') sont identifiable par $rdr->rx == 3 et les données pour la cinquième racine ('//customer') sont identifiable par $rdr->rx == 4. Dans l'exemple ci-dessous, le paramètre {branch => '*'} indique que le résultat sera du XML et le paramètre {branch => '+'} indique que le résultat 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 résultat: root0: Cust: Name = o'rob Street = pod alley City = no city Cust: Name = "sue" Street = baker street City = sidney Cust: Name = <smith> Street = high street City = boston Cust: Name = &jones Street = maple street City = new york Cust: Name = stewart Street = ring road City = "'&<A>'" path0: /data/customer /data/zzz/customer /data/order/database/customer /data/order/database/customer /data/order/database/customer root1: Supp: Name = ggg Supp: Name = hhh Supp: Name = iii Supp: Name = jjj root2: Xml: <customer id='444' name='o'rob'><street>pod alley</street><city>no city</city></customer> Xml: <customer id='111' name='"sue"'><street>baker street</street><city>sidney</city></customer> Xml: <customer id='652' name='<smith>'><street>high street</street><city>boston</city></customer> Xml: <customer id='184' name='&jones'><street>maple street</street><city>new york</city></customer> Xml: <customer id='520' name='stewart'><street>ring road</street><city>"'&<A>'"</city></customer> root3: P: <p><p>b1</p><p>b2</p></p> P: <p>b3</p> root4: Pyx: '(customer', 'Aid 444', 'Aname o'rob', '(street', '-pod alley', ')street', '(city', '-no city', ')city', ')customer' Pyx: '(customer', 'Aid 111', 'Aname "sue"', '(street', '-baker street', ')street', '(city', '-sidney', ')city', ')customer' Pyx: '(customer', 'Aid 652', 'Aname <smith>', '(street', '-high street', ')street', '(city', '-boston', ')city', ')customer' Pyx: '(customer', 'Aid 184', 'Aname &jones', '(street', '-maple street', ')street', '(city', '-new york', ')city', ')customer' Pyx: '(customer', 'Aid 520', 'Aname stewart', '(street', '-ring road', ')street', '(city', '-"'&<A>'"', ')city', ')customer' Nous pouvons également utiliser la fonction $rdr->value pour obtenir les mêmes données: 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 résultat: output0: Cust: Name = o'rob Street = pod alley City = no city Cust: Name = "sue" Street = baker street City = sidney Cust: Name = <smith> Street = high street City = boston Cust: Name = &jones Street = maple street City = new york Cust: Name = stewart Street = ring road City = "'&<A>'" output1: P: <p><p>b1</p><p>b2</p></p> P: <p>b3</p> Il faut dire ici que le résultat "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 déjà: cette ligne est "P: <p><p>b1</p><p>b2</p></p>". OPTION DUPATTL'option dupatt permet de lire multiples attributs avec le même nom dans une seule valeur. Le principe fonctionne par la concatenation des différentes valeurs dans une seule variable, séparé par un caractère spécial. La liste des attributs est ensuite trié par order alphabétique.L'option dupatt est valable uniquement si XML::Reader a été utilisé avec l'option XML::Parsepp. Par exemple, le code suivant concatène les attribut doublons avec le caractère "|": (La chaîne de caractères ($str) {dupatt => $str} est limité à des caractères Ascii visibles, sauf les caractères alpha-numériques, ' 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 résultat 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: EXEMPLESExaminons-nous le XML suivant, où nous souhaitons extraire les valeurs dans la balise <item> (c'est la première partie 'start...', et non pas la partie 'end...' qui nous intéresse), ensuite les attributs "p1" et "p3". La balise <item> doit ê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 résultat (c'est à dire la ligne 'dataz' / 'start9' ne fait pas partie du résultat): 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-startCi-dessous un programme pour parser le XML avec l'option {filter => 2, mode => 'attr-bef-start'}. (Notez que le préfixe '/start/param/data/item' est renseigné dans l'option {using =>} de la fonction new). En plus, nous avons besoins de 2 variables scalaires '$p1' et '$p3' pour enregistrer les paramètres '/@p1' et '/@p3' et les transférer dans la partie '$rdr->is_start' du programme, où 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-hashAvec 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 pyxAvec l'option {filter => 4, mode => 'pyx'}, par contre, le programme devient plus compliqué: Comme déjà montré dans l'exemple {filter => 2, mode => 'attr-bef-start'}, nous avons besoin de deux variables scalaires ('$p1' et '$p3') pour enregistrer les paramètres '/@p1' et '/@p3' et les transférer à l'endoit où on peut les afficher. En plus, nous avons besoin de compter les valeurs de texte (voir variable '$count' ci-dessous), afin d'identifier la première partie du texte 'start...' (ce que nous voulons afficher) et supprimer la deuxiè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 branchesOn peut combiner {filter => 5, mode => 'branches'} avec des expressions régulières pour parser les données 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'attributCi-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>don't leave me this way</street> <city>cambridge</city> </customer> </item> </database> <database loc="alpha"> <item> <customer name="smith" id="881"> <street>anyway</street> <city>big apple</city> </customer> <customer name="thatcher" id="504"> <street>baker street</street> <city>oxford</city> </customer> </item> </database> </data> }; 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 résultat: smith = 'rio|ny' , gates = 'dallas' smith = undef , gates = 'cambridge' smith = 'big apple' , gates = undef FONCTIONSFonction slurp_xmlLa fonction slurp_xml lit un fichier XML et aspire son contenu dans une référence à une liste. Voici un exemple où 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 paramètre de slurp_xml est ou le nom du fichier (ou une une référence à un scalaire, ou une référence à un fichier ouvert) du XML qu'on veut aspirer. Dans notre cas nous avons une référence à un scalaire \$line2. Le paramè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 éléments que nous souhaitons sélectionner, relative à la racine. Dans notre cas c'est ['@name', 'street', 'city']. Le paramètre suivant est la deuxième racine (définition root/branch), dans ce cas c'est root => '/data/supplier' avec branch => ['/']. Voici le résultat: 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 à XML::Simple, c'est à dire il lit toutes les données dans un seul coup dans une structure en mémoire. En revanche, la différence est que slurp_xml permet de spécifier les données qu'on veut avant de faire l'aspiration, ce qui résulte dans une structure en mémoire souvent plus petite et moins compliquée. On peut utiliser slurp_xml() avec des attributs doublons. Dans ce cas il faut faire deux choses: Premièrement il faut faire un "use XML::Reader" avec "qw(XML::Parsepp slurp_xml)". Deuxièmement il faut faire appel à 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 résultat: <data atr1='abc|ghi' atr2='def'></data> AUTEURKlaus Eichner, Mars 2009COPYRIGHT ET LICENSEVoici 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 ASSOCIESSi vous souhaitez écrire du XML, je propose d'utiliser le module "XML::Writer" ou "XML::MinWriter". Chacun de ces deux modules se présente avec une interface simple pour écrire un fichier XML. Si vous ne mélangez 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 résultat sera proprement formaté selon les règles XML.REFERENCESXML::TokeParser, XML::Simple, XML::Parser, XML::Parsepp, XML::Reader::RS, XML::Reader::PP, XML::Parser::Expat, XML::TiePYX, XML::Writer, XML::MinWriter.
Visit the GSP FreeBSD Man Page Interface. |