无法根据给定条件过滤 XML
Unable to filter XML based on given conditions
抱歉各位,我可能问了一个愚蠢的问题,但我不太精通 Perl 或 awk(shell 编程)。
我的要求是根据某些条件过滤XML。
作为参考,我提供了一个虚拟 XML:
<TRADEEXT>
<TRADE origin = "AB" ref = "1" version = "1"/>
<TRADE origin = "AB" ref = "1" version = "2"/>
<TRADE origin = "ABC" ref = "1" version = "1"/>
</TRADEEXT>
现在过滤条件如下:
仅必须选择来源 = "AB"
的交易
应用第一个条件后,确保仅选择那些基于 ref 的最高版本的交易(按 ref 分组)
因此经过过滤的 TRADES 的结果 XML 必须看起来像
<TRADEEXT>
<TRADE origin = "AB" ref = "1" version = "2"/>
</TRADEEXT>
我设法过滤了来源为 "AB" 的交易,如下面的代码所述。
但是我无法根据给定参考的最高版本过滤交易。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my $twig = new XML::Twig( twig_handlers => { TRADE => \&TRADE } );
$twig->parsefile('1513.xml');
$twig->set_pretty_print('indented');
$twig->print_to_file('out.xml');
sub TRADE {
my ($twig, $TRADE) = @_;
foreach my $c ($TRADE) {
$c->cut($TRADE) unless $c->att('origin') eq "AB";
}
}
任何提示将不胜感激。
好的,首先 - 你错过了一个技巧。
当您使用 twig_handler
时,它 'snips out' 是 XML 的一段,并将其传递给子例程,每次遇到子例程时都这样做。
所以 $TRADE
在你的 sub 中看到的是(已解析):
<TRADE origin = "AB" ref = "1" version = "1"/>
您的 foreach
循环因此没有任何意义。
您可以简化为:
sub TRADE {
my ( $twig, $TRADE ) = @_;
if ( not $TRADE -> att('origin') eq 'AB' ) {
$TRADE -> cut();
}
}
尽管如果您不 paste
ing,那么您可能想改用 delete()
。
由于要按照最高版本保存数据,所以在保存之前需要对整个批次进行检查。一种方法可能是 - 解析 XML 两次 - 一次提取最高版本。
但是当您使用 cut
时,您也许可以使用 paste
。
最清晰的方法是对 XML 数据进行两次遍历:第一次为每个 ref
找到最大值 version
,第二次删除所有内容版本低于最大值的元素。
此程序使用 TRADE
twig 处理程序构建每个 ref
的最大版本散列 %max_version
。完全不影响数据的解析。
然后 for
循环扫描 根元素 TRADEEXT
的所有 TRADE
子元素,使用 delete
删除所有那些版本不是最大版本的。
use strict;
use warnings;
use XML::Twig 3.48;
my $twig = new XML::Twig(
twig_handlers => { '/TRADEEXT/TRADE' => \&trade_handler },
att_accessors => [ qw/ origin ref version / ],
pretty_print => 'indented',
);
my %max_version;
$twig->parsefile('1513.xml');
for my $trade ($twig->root->children('TRADE')) {
my ($ref, $version) = ($trade->ref, $trade->version);
$trade->delete unless $version eq $max_version{$ref};
}
$twig->print_to_file('out.xml');
sub trade_handler {
my ($twig, $trade) = @_;
if ( $trade->origin eq 'AB' ) {
my ($ref, $version) = ($trade->ref, $trade->version);
unless (exists $max_version{$ref} and $max_version{$ref} >= $version) {
$max_version{$ref} = $version;
}
}
1;
}
输出
<TRADEEXT>
<TRADE origin="AB" ref="1" version="2"/>
</TRADEEXT>
抱歉各位,我可能问了一个愚蠢的问题,但我不太精通 Perl 或 awk(shell 编程)。
我的要求是根据某些条件过滤XML。
作为参考,我提供了一个虚拟 XML:
<TRADEEXT>
<TRADE origin = "AB" ref = "1" version = "1"/>
<TRADE origin = "AB" ref = "1" version = "2"/>
<TRADE origin = "ABC" ref = "1" version = "1"/>
</TRADEEXT>
现在过滤条件如下:
仅必须选择来源 = "AB"
的交易
应用第一个条件后,确保仅选择那些基于 ref 的最高版本的交易(按 ref 分组)
因此经过过滤的 TRADES 的结果 XML 必须看起来像
<TRADEEXT>
<TRADE origin = "AB" ref = "1" version = "2"/>
</TRADEEXT>
我设法过滤了来源为 "AB" 的交易,如下面的代码所述。 但是我无法根据给定参考的最高版本过滤交易。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my $twig = new XML::Twig( twig_handlers => { TRADE => \&TRADE } );
$twig->parsefile('1513.xml');
$twig->set_pretty_print('indented');
$twig->print_to_file('out.xml');
sub TRADE {
my ($twig, $TRADE) = @_;
foreach my $c ($TRADE) {
$c->cut($TRADE) unless $c->att('origin') eq "AB";
}
}
任何提示将不胜感激。
好的,首先 - 你错过了一个技巧。
当您使用 twig_handler
时,它 'snips out' 是 XML 的一段,并将其传递给子例程,每次遇到子例程时都这样做。
所以 $TRADE
在你的 sub 中看到的是(已解析):
<TRADE origin = "AB" ref = "1" version = "1"/>
您的 foreach
循环因此没有任何意义。
您可以简化为:
sub TRADE {
my ( $twig, $TRADE ) = @_;
if ( not $TRADE -> att('origin') eq 'AB' ) {
$TRADE -> cut();
}
}
尽管如果您不 paste
ing,那么您可能想改用 delete()
。
由于要按照最高版本保存数据,所以在保存之前需要对整个批次进行检查。一种方法可能是 - 解析 XML 两次 - 一次提取最高版本。
但是当您使用 cut
时,您也许可以使用 paste
。
最清晰的方法是对 XML 数据进行两次遍历:第一次为每个 ref
找到最大值 version
,第二次删除所有内容版本低于最大值的元素。
此程序使用 TRADE
twig 处理程序构建每个 ref
的最大版本散列 %max_version
。完全不影响数据的解析。
然后 for
循环扫描 根元素 TRADEEXT
的所有 TRADE
子元素,使用 delete
删除所有那些版本不是最大版本的。
use strict;
use warnings;
use XML::Twig 3.48;
my $twig = new XML::Twig(
twig_handlers => { '/TRADEEXT/TRADE' => \&trade_handler },
att_accessors => [ qw/ origin ref version / ],
pretty_print => 'indented',
);
my %max_version;
$twig->parsefile('1513.xml');
for my $trade ($twig->root->children('TRADE')) {
my ($ref, $version) = ($trade->ref, $trade->version);
$trade->delete unless $version eq $max_version{$ref};
}
$twig->print_to_file('out.xml');
sub trade_handler {
my ($twig, $trade) = @_;
if ( $trade->origin eq 'AB' ) {
my ($ref, $version) = ($trade->ref, $trade->version);
unless (exists $max_version{$ref} and $max_version{$ref} >= $version) {
$max_version{$ref} = $version;
}
}
1;
}
输出
<TRADEEXT>
<TRADE origin="AB" ref="1" version="2"/>
</TRADEEXT>