删除 perl 中除一个节点外的所有节点 XML::Twig

removing all but one node in perl XML::Twig

我有一个包含许多 level3 元素的 xml 文件。我想删除除一个这样的元素之外的所有元素。我的 xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<level1 id="level1_id">
    <level2 id="level2_id">
        <level3 id="level3_id1">
            <attributes>
                <attribute>1</attribute>
                <attribute>2</attribute>
            </attributes>
        </level3>
        <level3 id="level3_id2">
            <attributes>
                <attribute>1</attribute>
                <attribute>2</attribute>
            </attributes>
        </level3>
        <level3 id="level3_id3">
            <attributes>
                <attribute>1</attribute>
                <attribute>2</attribute>
            </attributes>
        </level3>
    </level2>
</level1>

我的 perl 脚本:

my $filename = 'test3.xml';
my $outfile = $filename."_after";
open my $output, '>', $outfile or die "Couldn't open output file\n";
my $twig = new XML::Twig (twig_handlers => { 'level2' => \&edit });
$twig->parsefile($filename);
#$twig->flush;
$twig->print($output);

sub edit {
    my ($twig, $element) = @_;
    my @elements= $element->children('level3');
    print $#elements."\n";
    my @elements= @elements[1..$#elements];
    print $#elements."\n";
    my $count = 0;
    foreach (@elements){
        $count++;
        $_->delete;
    }
    print $count;
    $twig->purge;

}

然而,这只留下了 level1 元素:

<?xml version="1.0" encoding="UTF-8"?>
<level1 id="level1_id"></level1>

另一方面,当顶层是 level2 时,我的脚本工作得很好。示例xml文件和处理后的结果:

<?xml version="1.0" encoding="UTF-8"?>

<level2 id="level2_id">
    <level3 id="level3_id1">
        <attributes>
            <attribute>1</attribute>
            <attribute>2</attribute>
        </attributes>
    </level3>
    <level3 id="level3_id2">
        <attributes>
            <attribute>1</attribute>
            <attribute>2</attribute>
        </attributes>
    </level3>
    <level3 id="level3_id3">
        <attributes>
            <attribute>1</attribute>
            <attribute>2</attribute>
        </attributes>
    </level3>
</level2>

结果:

<?xml version="1.0" encoding="UTF-8"?>
<level2 id="level2_id">
    <level3 id="level3_id1">
        <attributes>
            <attribute>1</attribute>
            <attribute>2</attribute>
        </attributes>
    </level3>
</level2>

这正是我想要的,即只剩下一个 level3 元素。我究竟做错了什么?这与我如何定义树枝处理程序有关吗? 我不想硬编码 xml 结构,例如我的 $twig = new XML::Twig (twig_handlers => { 'level1/level2' => \&edit }); 我不知道 level2 在实际 xml 文件中有多深,实际文件的结构可能不相同,所以这部分应该是动态的

不需要$twig->purge行或类似的东西,我不明白你为什么要写它

它将丢弃任何已解析但未打印到输出的内容,即您刚刚编辑的整个 level2 元素

我也推荐你写

my $twig = XML::Twig->new(
    twig_handlers => { level2 => \&edit },
    pretty_print  => 'indented',
);

因为您使用的 间接对象 语法不明确且容易出错。 pretty_print 选项还将使输出 XML 更具可读性。

我建议除非您特别想对大文件进行增量解析,否则 twig_handers 会不必要地复杂化。如果您想将 XML 视为流和其中的 modify/discard 部分,这是一个强大的工具,但实际上通常只是加载整个 XML,并且使用它更简单、更清晰。

您想要做的似乎是删除第一个之后的所有 'level3' 个元素。

所以:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;


my $twig = XML::Twig->new->parsefile('your_xml_file.xml');
my $count;

foreach my $level3 ( $twig->get_xpath('.//level3') ) {
   #delete after the first one. 
   $level3->delete if $count++;
}

#set formatting
$twig -> set_pretty_print('indented_a');
#print to stdout
$twig->print;