2011-10-27 36 views
2

我有一個像下面的一些XML文件:如何使用Perl的XML :: Twig刪除元素?

<machines> 
<server> 
    127.0.0.1 
</server> 
<proxy> 
    <ip>127.0.0.2</ip> 
    <etc>abc</etc> 
</proxy> 
</machines> 

,我想保持服務器並刪除他人,輸出應該是:

<machines> 
<server> 
127.0.0.1 
</server> 
</machines> 

我寫的腳本如下:

use warnings; 
use strict; 
use feature ':5.10'; 
use XML::Twig; 

my $path='C:\strawberry\perl\site\lib\file.xml'; 
my $filehandle; 
my $tweak_server =sub{ 
    my ($twig, $root) [email protected]_; 
    my $elt=$root; 
    while($elt=$elt->next_elt($root)){ 
     my $tag=$elt->tag; 
     say $tag; 
     if ($tag!~/server/){ 
      $elt->delete($tag);   
     }  
    } 
    $twig->flush; 
}; 




open($filehandle, "+<$path") or die "cannot open out file out_file:$!"; 
my $roots = { machines => 1 }; 
my $handlers = { 'machines' => $tweak_server, 
      }; 
my $twig = new XML::Twig(TwigRoots => $roots, 
       TwigHandlers => $handlers, 
       pretty_print => 'indented'#, 
       # twig_print_outside_roots => \*$filehandle 
       ); 
$twig->parsefile($path); 
close $filehandle; 

並得到了輸出:

server 
#PCDATA 
<machines> 
<server></server> 
<proxy> 
<ip>127.0.0.2</ip> 
<etc>abc</etc> 
</proxy> 
</machines> 

我真的不明白爲什麼會有「#PCDATA」,爲什麼它不能像我期望的那樣工作?

@mirod我試過如下:

use warnings; 
use strict; 
use feature ':5.10'; 
use XML::Twig; 

my $tweak_server =sub{ 
my ($twig, $root) [email protected]_; 
my $elt=$root; 
my $text=$elt->first_child_text('id'); 
if ($text=~m/12/){ 
    while($elt=$elt->next_elt('#ELT')){ 
     my $tag=$elt->tag; 
     say $tag; 
     if ($tag!~/id/){ 
      $elt->delete;   
     }  
    } 
} 
}; 

my $roots = { machines => 1 }; 
my $handlers = { 'machines/aaa' => $tweak_server, 
      }; 
my $twig =XML::Twig->new(TwigRoots => $roots, 
       TwigHandlers => $handlers, 
       pretty_print => 'indented'#, 
       # twig_print_outside_roots => \*$filehandle 
       ) 
    ->parse(\*DATA) 
    ->print; 
__DATA__ 

<machines> 
<server> 127.0.0.1 </server> 
<aaa> 
<id>12</id> 
<ip>127.0.0.2</ip> 
<option>127.0.0.6</option> 
<etc>abc</etc> 
</aaa> 
<aaa> 
<id>14</id> 
<ip>127.0.0.2</ip> 
<etc>abc</etc> 
</aaa> 
<aaa> 
<id>15</id> 
<ip>127.0.0.2</ip> 
<etc>abc</etc> 
</aaa> 
</machines> 

,輸出是:

<machines> 
<server> 127.0.0.1 </server> 
<aaa> 
<id>12</id> 
<option>127.0.0.6</option> 
<etc>abc</etc> 
</aaa> 
<aaa> 
<id>14</id> 
<ip>127.0.0.2</ip> 
<etc>abc</etc> 
</aaa> 
<aaa> 
<id>15</id> 
<ip>127.0.0.2</ip> 
<etc>abc</etc> 
</aaa> 
</machines> 

和我要的是刪除了三個要素,而不僅僅是一個:

<ip>127.0.0.2</ip> 
<option>127.0.0.6</option> 
<etc>abc</etc> 

下的元素

<id>12</id> 

有什麼建議嗎?

+1

你'#PCDATA',因爲這是「標籤」的文本內容。如果您想循環使用「真實」元素,請使用'$ elt-> next_elt('#ELT')'。 – mirod

+0

謝謝你,在twig.pm中,我剛剛找到= item next_elt($ optional_elt,$ optional_condition),所以'#ELT'可以成爲第一個參數? – trivial

+1

不,這兩個參數都是可選的,並且由於它們的類型不同(標量與XML :: Twig :: Elt),該方法可以確定使用哪個(哪些)。嘗試一下! – mirod

回答

2

下面將刪除proxy元素:

use warnings; 
use strict; 
use XML::Twig; 

my $str = ' 
<machines> 
<server> 
    127.0.0.1 
</server> 
<proxy> 
    <ip>127.0.0.2</ip> 
    <etc>abc</etc> 
</proxy> 
</machines> 
'; 

my $t = XML::Twig->new(
     twig_handlers => { 
      proxy => sub { $_->delete() }, 
     }, 
     pretty_print => 'indented', 
); 
$t->parse($str); 
$t->print($str); 
print "\n"; 

__END__ 

<machines> 
    <server> 
    127.0.0.1 
</server> 
</machines> 

如果您不希望打印出來server#PCDATA,然後擺脫say $tag;

+1

但除了代理和服務器之外,還有很多其他標籤我沒有列出來,但它是一種方式,謝謝你的回答。 – trivial

2

如果您的要求是隻保留服務器元素,那麼您可以通過將它們作爲twig_roots來告訴該模塊。這將對保持XML和服務器元素(及其內容)的根的效果,而放棄所有的休息:

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::Twig; 

XML::Twig->new(twig_roots => { server => 1 }, 
       pretty_print => 'indented', 
      ) 
     ->parse(\*DATA) 
     ->print; 

__DATA__ 
<machines> 
<server> 
    127.0.0.1 
</server> 
<proxy> 
    <ip>127.0.0.2</ip> 
    <etc>abc</etc> 
</proxy> 
</machines> 
+1

是的,它很好用,謝謝! – trivial