5

私の質問は、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');
4

3 に答える 3

4

ハンドラーに引数を渡す最も簡単で通常の方法は、クロージャーを使用することです。これは大きな言葉ですが、単純な概念です。このようにハンドラーを呼び出すと、ハンドラーtag => sub { handler( @_, $my_arg) }$my_arg渡されます。達成する閉鎖には、概念に関するより詳細な説明があります。

以下は、コードの書き方です。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
}
于 2010-07-14T06:25:10.200 に答える
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'
    ]
];

それが私が期待していたことです!

于 2010-07-12T10:29:00.950 に答える
1

免責事項: 私は自分で Twig を使用したことがないため、この回答は慣用的ではない可能性があります。これは一般的な「コールバック ハンドラで状態を保持する方法」の回答です。

ハンドラーとの間で情報をやり取りするには、次の 3 つの方法があります。

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

2。$t (XML::Twig オブジェクト) 自体に保持されている状態は、何らかの「状態」メンバーにあります。

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

三。ハンドラーをクロージャーにし、その状態を維持します

于 2010-07-12T10:00:48.283 に答える