XML :: Twig - set_text没有clobbering结构

时间:2015-06-12 12:02:04

标签: xml perl xml-twig

XML::Twig使用set_text方法时,会出现警告:

  

set_text($ string)   设置元素的文本:如果元素是PCDATA,只需设置其文本,否则剪切元素的所有子元素并为其创建一个PCDATA子元素,它保存文本。

所以如果我想做一些简单的事情,比如说 - 改变我的XML :: Document中所有文本的大小写:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new(
    'pretty_print'  => 'indented_a',
    'twig_handlers' => {
        '_all_' => sub {
            my $newtext = $_->text_only;
            $newtext =~ tr/[a-z]/[A-Z]/;
            $_->set_text($newtext);
        }
    }
);
$twig->parse( \*DATA );
$twig->print;

__DATA__
<root>
    <some_content>fish
        <a_subnode>morefish</a_subnode>
    </some_content>
    <some_more_content>cabbage</some_more_content>
</root>

这 - 由于set_text取代儿童 - 被摧毁为:

<root></root>

但如果我只关注一个(底层)节点(例如a_subnode),那么它可以正常工作。

是否有一种优雅的方法来替换/转换元素中的文本而不破坏它下面的数据结构?我的意思是,我可以测试儿童或类似事物的存在,但......似乎应该有更好的方法来做到这一点。 (可能是另一个图书馆?)

(为了清楚起见 - 这是我在文档中对所有文本进行音译的例子,我的实际用例相当复杂,但仍然是“文本转换”)。

我可能正在考虑使用节点剪切/粘贴方法(剪切所有子节点,替换文本,粘贴所有子节点),但这似乎是一种低效的方法。

2 个答案:

答案 0 :(得分:4)

不要在_all_上使用处理程序,而是只在文本元素#TEXT上使用,并将text_only更改为text。它应该工作。

更新:或者在创建树枝时使用char_handler选项:char_handler => sub { uc shift },而不是处理程序。

答案 1 :(得分:2)

我目前的做法是:

  • 迭代所有节点。
  • cut所有孩子。
  • 修改文字。
  • paste所有孩子。

这似乎效率低下,但似乎确实有效:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;
use Data::Dumper;

sub replace_text {
    my ( $twig, $element ) = @_;

    my $newtext = $element->text_only;
    my @children;
    foreach my $child ( $element->children ) {
        if ( not $child->tag eq "#PCDATA" ) {
            push( @children, $child->cut );
        }
    }
    $newtext =~ tr/[a-z]/[A-Z]/;
    $element->set_text($newtext);

    $_->paste( 'last_child', $element ) for @children;
}

my $twig =
    XML::Twig->new( 'twig_handlers' => { '_all_' => \&replace_text, } );
$twig->parse( \*DATA );

print "Result:\n";
$twig->print;

__DATA__
<root>
    <some_content>fish
        <a_subnode>morefish</a_subnode>
    </some_content>
    <some_more_content>cabbage</some_more_content>
</root>

这会将我的输出转换为:

<root><some_content>FISH
        <a_subnode>MOREFISH</a_subnode></some_content><some_more_content>CABBAGE</some_more_content></root>

因此,虽然它确实对节点进行了变换,但由于某种原因,它也会破坏输出格式。

重新解析:

XML::Twig -> new ( 'pretty_print' => 'indented_a' ) -> parse ( $twig -> sprint ) -> print;
似乎要做的伎俩。 (虽然重新格式化的双重解析似乎更不优雅)

<root>
  <some_content>FISH
        <a_subnode>MOREFISH</a_subnode></some_content>
  <some_more_content>CABBAGE</some_more_content>
</root>