无法根据给定条件过滤XML

时间:2015-01-08 15:11:05

标签: perl xml-twig

对不起,伙计们,我可能会问一个愚蠢的问题,但我不太熟悉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. 只有那些必须选择具有origin =“AB”

  2. 的TRADES
  3. 应用第一个条件后,请务必仅选择那些基于ref的最高版本的TRADES(按引用分组)

  4. 因此,带有过滤的TRADES的结果XML必须看起来像

    <TRADEEXT>
        <TRADE origin = "AB" ref = "1" version = "2"/>    
    </TRADEEXT>
    

    我设法过滤了原产地为“AB”的TRADES,如下面的代码所示。 但我无法根据给定参考​​的最高版本过滤TRADES。

    #!/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";
        }
    }
    

    任何提示都将受到高度赞赏。

2 个答案:

答案 0 :(得分:1)

执行此操作的最明确方法是对XML数据进行两次传递:第一次查找每个version的最大ref,第二次删除版本较少的任何元素比最大值。

此程序使用TRADE twig处理程序构建每%max_version个最大版本的哈希ref。它根本不会影响数据的解析。

然后for循环扫描根元素 TRADE的所有TRADEEXT子项,使用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>

答案 1 :(得分:0)

好的,首先 - 你错过了一个技巧。

当你使用twig_handler时,它会“剪掉”一段XML,并将其传递给子程序,并在每次遇到它时都这样做。

所以在你的sub中看到$TRADE是(解析):

<TRADE origin = "AB" ref = "1" version = "1"/>

因此,foreach循环没有任何意义。

您可以简化为:

sub TRADE {
    my ( $twig, $TRADE ) = @_;
    if ( not $TRADE -> att('origin') eq 'AB' ) {
        $TRADE -> cut();
     }
}

虽然如果你不是paste,那么你可能想要使用delete()

由于您希望根据最高版本保存数据,因此您需要先检查整个数据。一种方法可能是 - 解析XML两次 - 一次提取最高版本。

但是当您使用cut时,您可以使用paste