这个问题是所有在互联网上,但是我看到的例子不考虑我显然独特情况。 下面是我的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脚本过滤掉的节点。 例如:如果我想要类型为“错误”,以滤除所有的消息,我是使用XML,只有具有从上面的2行,我的输出将仅由上述警告消息。 这里显示的输出:
<message type="warning" from="Dcd_Mux" timestamp="Mon Dec 1 02:31:18 2014"> Could not connect to Dcd </message>
我需要如何开始打开XML,通过整个事情循环,并删除具有符合我的筛选器属性的任何节点的一些方向。 我感兴趣的是使用的libxml来完成这件事。
我使用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);
它可能看起来像这样使用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);
有两个元素,您的问题 - 先建立一个过滤条件,并在此基础上选择或删除元素。
特别 - 混合“添加”和“删除”可以说是相当困难的,因为决定做什么,如果他们不适用或矛盾可能相当烦人。
总之,我提供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>
该解决方案是从猎人麦克迈林的一个变化,并且在这里主要是为了说明我的意思是“看起来像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>