这个问题遍布互联网,但我看到的所有例子都没有考虑到我显而易见的独特情况。以下是我的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来完成这项工作。
答案 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>