2015-03-13 61 views
1

這個問題遍佈互聯網,但我看到的所有示例都沒有考慮到我顯然獨特的情況。這裏是我的XML摘錄:如何使用PERL腳本過濾掉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腳本的參數過濾掉節點。例如:如果我想過濾掉type =「error」的所有消息,並且我使用的是隻包含上面兩行的XML,那麼我的輸出將只是上面的警告消息。這裏顯示輸出:

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

我需要如何開始打開XML,通過整個事情循環,並刪除具有符合我的篩選器屬性的任何節點的一些方向。我有興趣使用LibXML來完成這件事。

+0

我不清楚自己需要什麼。例如,'perl logview.pl -type =「error」'應該發生什麼?你說它應該*「用'type =」error「'」*刪除所有消息,但是從什麼?你有一個你想編輯的靜態XML數據文件嗎? – Borodin 2015-03-13 21:05:47

+0

@Borodin我編輯了這個問題。我並不關心這些論據,但是要回答你的評論......是的,腳本直接訪問要解析/過濾的文件。 – 2015-03-13 21:39:13

回答

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); 
+1

OP的。 「我希望能夠根據perl腳本的參數過濾掉節點。」 – ikegami 2015-03-13 21:12:01

+1

我認爲這很有幫助,但一些解釋發生了什麼的評論可能會幫助更多。什麼是qw();在頂部? – 2015-03-13 21:45:46

+1

這是從模塊導入的東西的列表。在這種情況下,沒有什麼。如果還有其他不清楚的地方,請告訴我。 – ikegami 2015-03-13 21:46:37

1

它可以使用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

@ikegami:我對你的好鬥方式太熟悉了,並且期望它不符合Stack Overflow的人口衆多的利益來回答你。我再一次不得不將你的評論標記爲*非建設性*,並且希望你可以考慮你對Perl的普及的影響。 – Borodin 2015-03-13 21:23:55

+0

很好,請允許我重新表述:我已將您的評論標記爲非建設性的。 「看起來像Java」不是一種建設性的,即使它是真實的。然而,幾乎每一行都是一個沒有Java等價物的Perl成語。評論人: – ikegami 2015-03-13 21:30:15

+0

:糟糕的一天 - 心情不好? – jm666 2015-03-13 21:30:17

1

有兩個元素,您的問題 - 先建立一個篩選條件,選擇或基於它刪除元素。

特別是 - 混合'添加'和'刪除'可能是相當困難的,因爲決定如果不適用或相互矛盾會怎麼做可能會很煩人。

無論如何,我提供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> 
+0

不包括作爲問題一部分的參數。我確實看了一下XML :: Twig,但我不相信我們已經安裝了。我知道libxml在我們所有的機器上。我會進一步深入...也許我們可以添加它。 – 2015-03-13 21:42:50

+1

關於XML:Twig的主題,它附帶了命令行工具'xml_grep',如果該文件完全由要篩選的郵件組成,則可以使用 – ikegami 2015-03-13 21:55:09

+0

謝謝。看起來我們確實有xml_grep – 2015-03-13 22:18:16

0

該解決方案是從獵人麥克邁林的一個變化,並且在這裏主要是爲了說明我的意思通過「看起來像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> 
+0

@HunterMcMillen:我已經寫了這個作爲我對解決方案的評論的解釋。我希望它能幫助你理解我的想法。 – Borodin 2015-03-14 14:52:55