无法根据给定条件过滤 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>

现在过滤条件如下:

  1. 仅必须选择来源 = "AB"

  2. 的交易
  3. 应用第一个条件后,确保仅选择那些基于 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();
     }
}

尽管如果您不 pasteing,那么您可能想改用 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>