如何使用XML :: Twig跳过不需要的元素?

时间:2016-05-17 16:48:56

标签: perl xml-twig

尝试学习XML::Twig并从XML文档中获取一些数据。

我的XML包含20k + <ADN>个元素。 Eaach <ADN>元素包含数十个子元素,其中一个是<GID>。我只希望处理ADN == 1的那些GID。(参见示例XML是__DATA__

文档说:

  

处理程序按固定顺序触发,按类型(xpath)排序   首先是表达式,然后是regexps,然后是level),然后是表达式   指定一个完整的路径(从根元素开始)或不指定,然后通过   然后,表达式中的步骤数,然后是谓词数   谓词中的测试次数。最后一步没有的处理程序   指定在其他XPath处理程序之后触发的步骤(foo / bar / *)。   最后,所有处理程序最后被触发。

     

重要提示:一旦处理程序被触发,如果它返回0则为no   调用其他处理程序,但将调用的 all 处理程序除外   反正。

我的实际代码:

use 5.014;
use warnings;
use XML::Twig;
use Data::Dumper;

my $cat = load_xml_catalog();
say Dumper $cat;

sub load_xml_catalog {
        my $hr;
        my $current;
        my $twig= XML::Twig->new(
        twig_roots => {
            ADN => sub {      # process the <ADN> elements
                $_->purge;    # and purge when finishes with one
            },
        },
        twig_handlers => {
            'ADN/GID' => sub {
                return 1 if $_->trimmed_text == 1;
                return 0;     # skip the other handlers - if the GID != 1
            },

            'ADN/ID' => sub { #remember the ID as a "key" into the '$hr' for the "current" ADN
                $current = $_->trimmed_text;
                $hr->{$current}{$_->tag} = $_->trimmed_text;
            },

            #rules for the wanted data extracting & storing to $hr->{$current}
            'ADN/Name' => sub {
                $hr->{$current}{$_->tag} = $_->text;
            },
        },
        );
        $twig->parse(\*DATA);
    return $hr;
}
__DATA__
<ArrayOfADN>
    <ADN>
        <GID>1</GID>
        <ID>1</ID>
        <Name>name 1</Name>
    </ADN>
    <ADN>
        <GID>2</GID>
        <ID>20</ID>
        <Name>should be skipped because GID != 1</Name>
    </ADN>
    <ADN>
        <GID>1</GID>
        <ID>1000</ID>
        <Name>other name 1000</Name>
    </ADN>
</ArrayOfADN>

输出

$VAR1 = {
          '1000' => {
                    'ID' => '1000',
                    'Name' => 'other name 1000'
                  },
          '1' => {
                 'Name' => 'name 1',
                 'ID' => '1'
               },
          '20' => {
                  'Name' => 'should be skipped because GID != 1',
                  'ID' => '20'
                }
        };

所以,

  • 当GID!= 1时,ADN/GID的处理程序返回0
  • 为什么还要调用其他处理程序?
  • 预期(通缉)输出没有'20' => ...
  • 如何正确跳过不需要的节点?

1 个答案:

答案 0 :(得分:4)

&#34;返回零&#34;在这种情况下,事情有点像红鲱鱼。如果您的元素有多个匹配项,那么一个返回零会阻止其他元素匹配。

这并不意味着它仍然不会尝试和处理后续节点。

我认为您感到困惑 - 您拥有处理<ADN>元素的单独子元素的处理程序 - 它们会分别触发。这是设计的。 xpath有一个优先顺序,但仅限于重复匹配。你的完全是分开的,所以他们都会开火#39;因为它们会触发不同的元素。

但是,您可能会发现知道 - twig_handlers允许xpath表达式很有用 - 所以您可以明确地说:

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

use XML::Twig;
my $twig = XML::Twig->parse( \*DATA );
$twig -> set_pretty_print('indented_a');

foreach my $ADN ( $twig -> findnodes('//ADN/GID[string()="1"]/..') ) {
   $ADN -> print;
}

这也适用于twig_handlers语法。我建议做一个处理程序只有在你需要预处理XML或者你的内存受限时才真正有用。有20,000个节点,你可能会。 (此时purge是你的朋友)。

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

use XML::Twig;
my $twig = XML::Twig->new(
   pretty_print  => 'indented_a',
   twig_handlers => {
      '//ADN[string(GID)="1"]' => sub { $_->print }
   }
);

$twig->parse( \*DATA );


__DATA__
<ArrayOfADN>
    <ADN>
        <GID>1</GID>
        <ID>1</ID>
        <Name>name 1</Name>
    </ADN>
    <ADN>
        <GID>2</GID>
        <ID>20</ID>
        <Name>should be skipped because GID != 1</Name>
    </ADN>
    <ADN>
        <GID>1</GID>
        <ID>1000</ID>
        <Name>other name 1000</Name>
    </ADN>
</ArrayOfADN>

虽然,我可能会这样做:

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

use XML::Twig;

sub process_ADN {
    my ( $twig, $ADN ) = @_; 
    return unless $ADN -> first_child_text('GID') == 1;
    print "ADN with name:", $ADN -> first_child_text('Name')," Found\n";
}


my $twig = XML::Twig->new(
   pretty_print  => 'indented_a',
   twig_handlers => {
      'ADN' => \&process_ADN
   }
);

$twig->parse( \*DATA );


__DATA__
<ArrayOfADN>
    <ADN>
        <GID>1</GID>
        <ID>1</ID>
        <Name>name 1</Name>
    </ADN>
    <ADN>
        <GID>2</GID>
        <ID>20</ID>
        <Name>should be skipped because GID != 1</Name>
    </ADN>
    <ADN>
        <GID>1</GID>
        <ID>1000</ID>
        <Name>other name 1000</Name>
    </ADN>
</ArrayOfADN>