2010-07-12 6 views
5

ma question est: comment passer certains arguments à XML: le gestionnaire de Twig, et comment retourner le résultat du gestionnaire.Comment puis-je passer des arguments et renvoyer des valeurs avec le gestionnaire de XML :: Twig?

Voici mon code, qui hardcoded:

<counter name = "music", report type = "month", stringSet index = 4>. Comment implémenter cela en utilisant des arguments $counter_name, $type, $id? et comment retourner le résultat de string_list? Merci (désolé je n'ai pas posté le fichier xml ici parce que j'ai un peu de mal à le faire, quoi que ce soit au sein de < et> sont ignorés).

use XML::Twig; 

sub parse_a_counter { 

    my ($twig, $counter) = @_; 
    my @report = $counter->children('report[@type="month"]'); 

    for my $report (@report){ 

     my @stringSet = $report->children('stringSet[@index=”4”]'); 
     for my $stringSet (@stringSet){ 

      my @string_list = $stringSet->children_text('string'); 
      print @string_list; # in fact I want to return this string_list, 
            # not just print it. 
     } 
    } 

    $counter->flush; # free the memory of $counter 
} 

my $roots = { 'counter[@name="music"]' => 1 }; 

my $handlers = { counter => \&parse_a_counter }; 

my $twig = new XML::Twig(TwigRoots => $roots, 
         TwigHandlers => $handlers); 

$twig->parsefile('counter_test.xml'); 
+0

@ lilili07: Bienvenue à SO. J'ai formaté votre code, vous pouvez inclure le XML en l'entourant de backticks. N'hésitez pas à le modifier si j'ai fait une erreur. – Zaid

Répondre

1

DISCLAIMER: Je n'ai pas utilisé Brindille moi-même, cette réponse pourrait ne pas être idiomatiques - il est une réponse générique « comment puis-je garder l'état dans un gestionnaire de rappel ».

Trois façons d'information entrant et sortant des gestionnaires sont:

ONE. Etat tenue dans un endroit statique

package TwigState; 

my %state =(); 
# Pass in a state attribute to get 
sub getState { $state{$_[0]} } 
# Pass in a state attribute to set and a value 
sub setState { $state{$_[0]} = $_[1]; } 

package main; 

sub parse_a_counter { # Better yet, declare all handlers in TwigState 
    my ($twig, $element) = @_; 
    my $counter = TwigState::getState('counter'); 
    $counter++; 
    TwigState::setState('counter', $counter); 
} 

DEUX. Etat tenue dans un t $ (XML :: Twig objet) lui-même dans une "État" membre

# Ideally, XML::Twig or XML::Parser would have a "context" member 
# to store context and methods to get/set that context. 
# Barring that, simply make one, using a VERY VERY bad design decision 
# of treating the object as a hash and just making a key in that hash. 
# I'd STRONGLY not recommend doing that and choosing #1 or #3 instead, 
# unless there's a ready made context data area in the class. 
sub parse_a_counter { 
    my ($twig, $element) = @_; 
    my $counter = $twig->getContext('counter'); 
    # BAD: my $counter = $twig->{'_my_context'}->{'counter'}; 
    $counter++; 
    TwigState::setState('counter', $counter); 
    $twig->setContext('counter', $counter); 
    # BAD: $twig->{'_my_context'}->{'counter'} = $counter; 
} 

# for using DIY context, better pass it in with constructor: 
my $twig = new XML::Twig(TwigRoots => $roots, 
         TwigHandlers => $handlers 
         _my_context => {}); 

TROIS. Faire le gestionnaire et un closure avoir à maintenir l'état de cette façon

+0

Je prévoyais d'ajouter un exemple de fermeture plus tard, mais ressemble à draegtun me battre dans une réponse séparée – DVK

1

La façon la plus simple est de faire __parse_a_counter__ retour sous (ie. Fermeture) et stocker les résultats dans une variable globale. Par exemple:

use strict; 
use warnings; 
use XML::Twig; 

our @results;  # <= put results in here 

sub parse_a_counter { 
    my ($type, $index) = @_; 

    # return closure over type & index 
    return sub { 
     my ($twig, $counter) = @_; 
     my @report = $counter->children(qq{report[\@type="$type"]}); 

     for my $report (@report) { 
      my @stringSet = $report->children(qq{stringSet[\@index="$index"]}); 

      for my $stringSet (@stringSet) { 
       my @string_list = $stringSet->children_text('string'); 
       push @results, \@string_list; 
      } 
     } 
    }; 
} 

my $roots = { 'counter[@name="music"]' => 1 }; 
my $handlers = { counter => parse_a_counter("month", 4) }; 

my $twig = XML::Twig->new(
    TwigRoots => $roots,      
    TwigHandlers => $handlers, 
)->parsefile('counter_test.xml'); 

Je l'ai testé cela avec le code XML suivant (qui est ce que je pouvais travailler à partir de votre exemple de code XML &):

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <counter name="music"> 
     <report type="week"> 
      <stringSet index="4"> 
       <string>music week 4</string> 
      </stringSet> 
     </report> 
    </counter> 
    <counter name="xmusic"> 
     <report type="month"> 
      <stringSet index="4"> 
       <string>xmusic month 4</string> 
      </stringSet> 
     </report> 
    </counter> 
    <counter name="music"> 
     <report type="month"> 
      <stringSet index="4"> 
       <string>music month 4 zz</string> 
       <string>music month 4 xx</string> 
      </stringSet> 
     </report> 
    </counter> 
</root> 

Et je suis rentré ceci:

[ 
    [ 
     'music month 4 zz', 
     'music month 4 xx' 
    ] 
]; 

C'est ce à quoi je m'attendais!

4

La manière la plus simple et la plus courante de transmettre des arguments aux gestionnaires consiste à utiliser des fermetures. C'est un grand mot mais un concept simple: vous appelez le gestionnaire comme ceci tag => sub { handler(@_, $my_arg) } et $my_arg sera transmis au gestionnaire. Achieving Closure a des explications plus détaillées sur le concept.

Voici comment j'écrirais le code. J'ai utilisé Getopt::Long pour le traitement des arguments, et qq{} à la place des guillemets autour des chaînes qui contenaient une expression XPath, pour pouvoir utiliser les guillemets dans l'expression.

#!/usr/bin/perl 
use strict; 
use warnings; 

use XML::Twig; 

use Getopt::Long; 

# set defaults 
my $counter_name= 'music'; 
my $type= 'month'; 
my $id= 4; 

GetOptions ("name=s" => \$counter_name, 
      "type=s" => \$type, 
      "id=i" => \$id, 
      ) or die; 

my @results; 

my $twig= XML::Twig->new( 
      twig_roots => { qq{counter[\@name="$counter_name"]} 
          => sub { parse_a_counter(@_, $type, $id, \@results); } }) 
        ->parsefile('counter_test.xml'); 

print join("\n", @results), "\n"; 

sub parse_a_counter { 

    my ($twig, $counter, $type, $id, $results) = @_; 
    my @report = $counter->children(qq{report[\@type="$type"]}); 

    for my $report (@report){ 

     my @stringSet = $report->children(qq{stringSet[\@index="$id"]}); 
     for my $stringSet (@stringSet){ 

      my @string_list = $stringSet->children_text('string'); 
      push @$results, @string_list; 
     } 
    } 

    $counter->purge; # free the memory of $counter 
} 
Questions connexes