Perl,删除XML节点

时间:2015-10-20 10:43:32

标签: xml perl xml-simple

data.xml中

<people>
  <person name="John">
     <param name="age" value="21" />
  </person>
  <person name="Jane">
     <param name="age" value="25" />
  </person>
</people>

我有这段XML。我正在编写一个脚本,以便将<person>个节点附加到<people>节点。我使用XML :: Simple

(请不要建议我使用另一个库,我意识到它的困难)。

my $remove_person = "Jane";

my $xml = XMLin('data.xml', ForceArray => 1, KeepRoot => 1, KeyAttr => []);
if(exists $xml->{people}[0]{person}){
        my $var = $xml->{people}[0]{person};
        my $count = @$var;
        my $person_index = 0;
        for(my $i = 0; $i < $count; $i++){
                if($xml->{people}[0]{person}[$i]->{name} eq $remove_person){
                        print "Person found at " . $person_index . " index";
                        $person_index = $i;
                        $person_to_remove = $xml->{people}[0]{person}[$i];
                }
        }
} else {
        print "Person not found in data.xml\r";
}

上面的代码将给出我想删除的节点的索引。 从这一点来说,我遇到了麻烦。我无法想出从数据中删除此索引的正确方法。到目前为止,我已经尝试了一种使用splice的方法,它返回了我要删除的XML部分,然后我使用XMLout()将数组转换回XML。使用=~ s///g,我能够编辑节点更改(<person>变为<opt>)。一旦我XMLout()'ed原始的data.xml结构,我试图用原始结构的空字符串替换XML的可移动部分的变量。

显然,这没有用。

my $new_xml    = XMLout($xml, KeepRoot => 1);
my $remove_xml = XMLout($person_to_remove, KeepRoot => 1);

$remove_xml =~ s/opt/person/g;
$new_xml =~ s/($remove_xml)//g; # facepalm, i know

如何我会删除这部分XML,无论是通过删除数组数据还是删除纯文本文本,以便将新结构写回原始的data.xml文件?

3 个答案:

答案 0 :(得分:1)

编辑:以下是之前发布的在这个问题中添加了“请不建议我使用其他库”。我要离开了,因为我仍然认为正确答案是“不要使用XML::Simple”。您可以使用锤子将螺丝钉在墙上,但是它并没有改变这样一个事实:无论你多么努力,结果都会变得混乱。

不要使用XML::Simple,这非常简单。即使XML::Simple说:

  

不鼓励在新代码中使用此模块。其他模块可用,提供更直接和一致的接口。

基本问题是只能通过散列和数组直接表示简单(简单!)的XML。如果你考虑一下 - XML允许同一父节点下面的重复节点,但具有不同的属性和内容。它还允许一元标签。

如何使用XML::Twig代替:

#!/urs/bin/env perl
use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig -> new ('pretty_print' => 'indented_a' ) -> parsefile ( 'your_xml' ); 
foreach my $element ( $twig -> get_xpath('person[@name="Jane"]') ) {
   $element -> delete;
}

$twig -> print; 

您可以 - 如果需要 - 也可以使用parsefile_inplace通过内部编辑执行此操作。否则,打开一个新文件并通过$twig -> sprint输出新的XML。

e.g:

XML::Twig->new(
    'pretty_print'  => 'indented_a',
    'twig_handlers' => {
        'person[@name="Jane"]' => sub { $_->delete }
    }
)->parsefile_inplace('xml_filename.xml');

如果您打算使用锤子作为螺钉 - 这应该使用您的初始代码和XML::Simple

$xml->{people}[0]{person} = 
     [ grep { not $_->{name} eq $remove_person }
                      @{ $xml->{people}[0]{person} } ];

使用name属性上的过滤数组替换相关数组。

输出:

<people>
  <person name="John">
    <param name="age" value="21" />
  </person>
</people>

答案 1 :(得分:0)

由于你已经toldXML::Simple的要点是使用Perl数据结构而不是字符串操作。所以,忘记s///并尝试

my $xml = XMLin($data, ForceArray => 1, KeepRoot => 1);
my $remove = 'Jane';
delete $xml->{people}[0]{person}{$remove};
print XMLout($xml, KeepRoot => 1);

或,空KeyAttr

my $xml = XMLin($data, ForceArray => 1, KeepRoot => 1, KeyAttr => []);
@{ $xml->{people}[0]{person} } = grep $_->{name} ne $remove,
                                 @{ $xml->{people}[0]{person} };
print XMLout($xml, KeepRoot => 1);

为了进行比较,XML::XSH2中的相同任务:

 open data.xml ;
 my $remove = 'Jane' ;
 delete /people/person[@name=$remove] ;
 save :b ;

答案 2 :(得分:0)

可悲的是,我最终遇到了同样的问题,我不得不在没有附加库的情况下在AIX上编辑一些XML。我最终删除了这样的东西

perl -0777 -p -i -e "s;(<HARDWARE>.*)<DESCRIPTION>.*<\/DESCRIPTION>(.*<\/HARDWARE>);\$1\$2;s" my.xml

这太丑了。我不喜欢它。但它确实有效,并且让你知道如何编写一个应该立即执行的regexpr。