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

标签 perl xml-twig

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

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

文档说:

Handlers are triggered in fixed order, sorted by their type (xpath expressions first, then regexps, then level), then by whether they specify a full path (starting at the root element) or not, then by number of steps in the expression , then number of predicates, then number of tests in predicates. Handlers where the last step does not specify a step (foo/bar/*) are triggered after other XPath handlers. Finally all handlers are triggered last.

Important: once a handler has been triggered if it returns 0 then no other handler is called, except a all handler which will be called anyway.

我的实际代码:

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

所以,

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

最佳答案

在这种情况下,“返回零”的事情有点转移注意力。如果您的元素上有多个匹配项,那么其中一个返回零将抑制其他匹配项。

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

我认为您感到困惑 - 您有 <ADN> 的单独子元素的处理程序元素 - 并且它们单独触发。这是设计使然。 xpath 有一个优先顺序但仅限于重复的比赛。不过,您的完全独立,因此它们都会“触发”,因为它们会触发不同的元素。

但是,您可能会发现了解 - 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 就是你的 friend )。

#!/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>

关于perl - 如何使用 XML::Twig 跳过不需要的元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37282103/

相关文章:

perl - 对对象进行排序

XML::Twig 使用 twig handlers 或 twig root 更新部分 xml 文件

Perl XML::Twig 问题请

perl - 使用 perl 的 reduce 计算点积

perl - 模块和主脚本中使用的命令行选项

perl - 为什么版本排序顺序颠倒了?

perl - 使用 Perl 模块与使用 system() 调用

perl - 在 XML :TWIG, 中如何在找到感兴趣的元素后停止解析

perl - 使用 XML::Twig 处理大文件