2011-07-07 56 views
4

我試圖寫在Perl子程序時的一些子節點的文本值規定將在XML刪除一個給定節點使用。定影的XPath謂詞在XML ::嫩枝

鑑於XML,如:

<Path> 
    <To> 
    <My> 
     <Node> 
     <ChildA>ValA</ChildA> 
     <ChildB>ValB</ChildB> 
     <ChildC>ValC</ChildC> 
     </Node> 
    </My> 
    </To> 
</Path> 
<!-- A lot of siblings follow... --> 

我使用XPath表達式基本上是:

/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"] 

當我試圖運行我的劇本,我發現了一個錯誤,如:

Error in XPath expression 
/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"] at 
ChildA="ValA" and ChildB="ValB" and ChildC="ValC" at Twig.pm line 3353 

我很茫然,我正在尋找建議。我試着用搜索引擎周圍,但我無法找到工作的努力在XML::Twig使用謂詞像這樣的例子。我不知道問題出在我的XPath語法或如何我使用XML::Twig

良好的措施,我也試着:

/Path/To/My/Node[ChildA/text()="ValA" and ChildB/text()="ValB" and ChildC/text()="ValC"] 

沒有運氣,要麼。解決辦法是什麼?

+0

您使用與'findnodes'或作爲一個'twig_handlers'觸發這體現在哪裏? 'findnodes',由肖恩提到,將允許您使用所有的XPath的,只要你使用XML ::嫩枝:: XPath的。觸發處理程序的謂詞更加有限,您需要有一個處理'Node'的測試孩子值的處理程序。 – mirod

回答

3

內的測試,Node是上下文節點,所以你不得不說:

/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"] 

這在使用XML::XPath一個短的測試程序爲我工作。

編輯:對不起,我不太熟悉XML ::嫩枝,和我做了關於它的XPath能力的不正確的假設。根據文檔,它只支持一種「類XPath」的語法,不會升到您示例的複雜程度。但是,如果使用XML::Twig::XPath而不是XML::Twig,你得到充分的XPath引擎:

my $twig = XML::Twig::XPath->new; 
$twig->parse('your string'); 
my $nodes = $twig->findnodes('/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"]'); 
print $nodes; 

這版畫 「ValAValBValC」。

+0

同樣的錯誤,只有現在錯誤有「./」 – Dave

3

有2種方法可以做到這一點:通過加載整個XML和刪除你不想要的節點,然後輸出樹枝,或通過過濾,你走,這是一個稍微複雜一些,但使用較少的內存。

第一種方式

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::Twig::XPath; 

my $t= XML::Twig::XPath->new(pretty_print => 'indented') 
         ->parse(\*DATA); 
$_->delete for ($t->findnodes('/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"]')); 

$t->print; 

__DATA__ 
<Path> 
    <To> 
    <My> 
     <Node> 
     <ChildA>ValA</ChildA> 
     <ChildB>ValB</ChildB> 
     <ChildC>ValC</ChildC> 
     </Node> 
     <Node> 
     <ChildA>ValD</ChildA> 
     <ChildB>ValB</ChildB> 
     <ChildC>ValC</ChildC> 
     </Node> 
    </My> 
    </To> 
</Path> 
(你可能需要一個最新的XML版本:: XPathEngine,我還沒有跟舊的或XML :: XPath,它也可以作爲XPath引擎進行了測試)

與「過濾器」的方式:

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::Twig; 

XML::Twig->new(twig_roots => { '/Path/To/My/Node' => \&filter }, 
       twig_print_outside_roots => 1, 
       keep_spaces => 1, 
      ) 
     ->parse(\*DATA); 
exit; 

# the handler expressions cannot lookahead, so we need to look at each node 
# once it's completely parsed 
sub filter 
    { my($t, $node)= @_; 
    if( ($node->field('ChildA') eq 'ValA') 
     && ($node->field('ChildB') eq 'ValB') 
     && ($node->field('ChildC') eq 'ValC') 
    ) 
     { $node->delete; } 
    else 
     { $t->flush; } 
    } 

__DATA__ 
<Path> 
    <To> 
    <My> 
     <Node> 
     <ChildA>ValA</ChildA> 
     <ChildB>ValB</ChildB> 
     <ChildC>ValC</ChildC> 
     </Node> 
     <Node> 
     <ChildA>ValD</ChildA> 
     <ChildB>ValB</ChildB> 
     <ChildC>ValC</ChildC> 
     </Node> 
    </My> 
    </To> 
</Path>