2010-07-12 57 views
5

我的问题是:如何将一些参数传递给XML:Twig的处理程序,以及如何从处理程序返回结果。如何传递参数并使用XML :: Twig的处理程序返回值?

这里是我的代码,它硬编码:

<counter name = "music", report type = "month", stringSet index = 4>

如何通过使用参数$counter_name$type,$id来实现此目的?以及如何返回string_list的结果?谢谢(对不起,我没有在这里发布xml文件,因为我遇到了一些麻烦,<和>中的任何内容都被忽略)。

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:欢迎来到SO。我对代码进行了格式化,您可以通过将其包含在反引号中来包含XML。如果我犯了一个错误,请随意编辑它。 – Zaid 2010-07-12 09:32:44

回答

1

免责声明:我没有用嫩枝自己,所以这个答案可能不是地道的 - 它是一个通用的“我怎么保持状态回调处理程序”的答案。

三个进出处理器的传递信息的方法是:

之一。在静态位置

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

保持两州。在一些 “国家” 成员

# 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 => {}); 

THREE在$ T(XML ::嫩枝对象)本身保持状态。使处理程序为closure并让它保持这种状态

+0

我打算在后面添加一个闭包例子,但看起来像draegtun在单独的答案中打败了我 – DVK 2010-07-12 15:20:37

1

最简单的方法是使__parse_a_counter__返回一个子(即闭包)并将结果存储在全局变量中。例如:

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

我测试了用下面的XML(这是我可以从你的示例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> 

而且我回到这一点:

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

这就是我期待的!

4

将参数传递给处理程序的最简单且常用的方法是使用闭包。这是一个很大的词,但一个简单的概念:你打电话给这样的处理程序,这样tag => sub { handler(@_, $my_arg) }$my_arg将被传递给处理程序。 Achieving Closure对此概念有更详细的解释。

以下是我将如何编写代码。我使用Getopt::Long进行参数处理,使用qq{}而不是围绕包含XPath表达式的字符串引号,以便能够在表达式中使用引号。

#!/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 
} 
相关问题