-3

数据.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";
}

上面的代码将为我提供我希望删除的节点的索引。从这一点开始,我遇到了麻烦。我无法找到从数据中删除此索引的正确方法。
到目前为止,我已经尝试了一种 using 的方法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 文件?

4

3 回答 3

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::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>
于 2015-10-20T10:49:23.020 回答
0

正如您已经知道的那样,XML::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 ;
于 2015-10-20T11:44:31.383 回答
0

遗憾的是,我最终遇到了大致相同的问题,我不得不在 AIX 上编辑一些 XML,而无需额外的库。我最终删除了这样的东西

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

这很丑陋。我不喜欢它。但它当时起作用了,并且只要你知道如何编写一个应该不时做的正则表达式。

于 2015-12-04T08:34:23.000 回答