如何使用PERL脚本从XML中过滤出节点

时间:2015-03-13 19:36:16

标签: xml perl filter attributes libxml2

这个问题遍布互联网,但我看到的所有例子都没有考虑到我显而易见的独特情况。以下是我的XML摘录:

<message type="error" from="Realtime" timestamp="Mon Nov 24 19:28:55 2014"> Could not receive from Loader </message>
<message type="warning" from="Dcd_Mux" timestamp="Mon Dec  1 02:31:18 2014"> Could not connect to Dcd </message>

我没有多个级别的节点,而是在消息节点上只有几个属性。我希望能够根据我的Perl脚本的参数过滤掉节点。例如:如果我想过滤掉所有类型=&#34;错误&#34;的消息,并且我使用的XML只有上面的2行,那么我的输出只是上面的警告消息。此处显示的输出:

<message type="warning" from="Dcd_Mux" timestamp="Mon Dec  1 02:31:18 2014"> Could not connect to Dcd </message>

我需要一些指导,如何开始打开XML,循环遍历整个事物,并删除任何具有与我的过滤器匹配的属性的节点。我有兴趣使用LibXML来完成这项工作。

4 个答案:

答案 0 :(得分:2)

使用XML::LibXML可能看起来像这样:

use strict;
use warnings; 

use XML::LibXML;

my $filename = $ARGV[0] 
   or die "Missing XML filename to parse";
my $type = $ARGV[1] 
   or die "Missing type of node to exclude";

open(my $xml_file, '<', $filename) 
   or die "Cannot open XML file '$filename' for reading: $!";

my $dom = XML::LibXML->load_xml(IO => $xml_file);
NODE:
foreach my $message_node ( $dom->findnodes('/root/message') ) {
   next NODE 
      unless $message_node->hasAttribute('type');

   $message_node->unbindNode() 
      if $message_node->getAttribute('type') eq $type;
}
$dom->toFile($filename);

答案 1 :(得分:2)

您的问题有两个要素 - 首先构建过滤条件,然后根据它选择或删除元素。

特别是 - 混合&#39;添加&#39;和&#39;删除&#39;可能会非常困难,因为如果他们不适用或反对决定该怎么做可能会相当烦人。

无论如何,我提供XML::Twig,尽管这不是你所要求的 - 因为我已经用了很多,并没有真正感动过的libxml。

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

use XML::Twig;

#read these from ARGV, just here as example.
my @sample_filters = qw ( -type=error
                          -from=Not_Dcd_Mux );

my %exclude;
for (@sample_filters) {
    if (m/^-/) {
        my ( $att, $criteria ) = (
            m/^-     #starts with -
              (\w+)  #word
              =     
              (\w+)
              $      #end of string
              /x
        );
        next unless $att;
        $exclude{$att} = $criteria;
    }
}

#process_message is called for each 'message' element, and tests filters for exclusion.
sub process_message {
    my ( $twig, $message ) = @_;
    foreach my $att ( keys %exclude ) {
        if ( $message->att($att) eq $exclude{$att} ) {
            $message->delete();
            last;
        }
    }
}

my $twig = XML::Twig->new(
    pretty_print  => 'indented',
    twig_handlers => { 'message' => \&process_message }
);
$twig->parse( \*DATA ); #might use 'parsefile ( $filename )' or 'STDIN' instead
$twig->print;


__DATA__
<XML>
<message type="error" from="Realtime" timestamp="Mon Nov 24 19:28:55 2014"> Could not receive from Loader </message>
<message type="warning" from="Not_Dcd_Mux" timestamp="Mon Dec  1 02:31:18 2014"> Could not connect to Dcd </message>
<message type="warning" from="Dcd_Mux" timestamp="Mon Dec  1 02:31:18 2014"> Could not connect to Dcd </message>
</XML>

答案 2 :(得分:0)

我使用XML::LibXML作为我的XML解析器。

use XML::LibXML qw( );

die "usage\n" if @ARGV != 2;

my ($type, $qfn) = @ARGV;
my $doc = XML::LibXML->new->parse_file($qfn);
for my $node ($doc->findnodes('//message') {
   my $type_addr = $node->getAttribute('type');
   next if !$type_addr || $type_addr ne $type;

   $node->parentNode->removeChild($node);
}

$doc->toFile($qfn);

答案 3 :(得分:0)

此解决方案是来自 Hunter McMillen 的解决方案的变体,在这里主要是为了说明我的意思“看起来像用Perl编写的Java程序”

参数验证是它的一部分,虽然我把它简化为简单的计数检查,但我通常不会写任何东西。值得怀疑的是,问题是如何处理数据,任何此类修改都取决于谁将使用该程序以及频率。

我选择序列化输出并将其打印到STDOUT,因为在命令行上根据需要重定向输出通常更有用。

我认识到我认为通过注意验证和一般“保护我自己”的Java风格方法。我不相信添加标签并在next中使用它会有所帮助,特别是在这么短的循环中。

use strict;
use warnings; 

use XML::LibXML::PrettyPrint;

@ARGV == 2 or die <<END_USAGE;
Usage:
  $0 <XML file> <node type>
END_USAGE

my ($xml_file, $exclude_type) = @ARGV;

my $dom = XML::LibXML->load_xml(location => $xml_file);

for my $node ( $dom->findnodes('/root/message[@type]') ) {
  my $type = $node->getAttribute('type');
  $node->unbindNode if $type eq $exclude_type;
}

local $XML::LibXML::skipXMLDeclaration = 1;
my $pp = XML::LibXML::PrettyPrint->new;
print $pp->pretty_print($dom)->toString;

<强>输出

<root>
  <message type="warning" from="Dcd_Mux" timestamp="Mon Dec  1 02:31:18 2014">
    Could not connect to Dcd
  </message>
</root>