XML::Twig 在 T 恤上的错误输出

Buggy output from XML::Twig on a Tee

我正在尝试将一个 xml 文件拆分成多个格式正确的片段,并且 an ancient PerlMonks solution 在 XML::Twig 的帮助下做得非常好一个 T 恤……至少需要简单的数据输入。

如果我通过重新组合节点以过滤到父节点来使数据结构稍微复杂一点,则第二个文件的格式不正确:父节点缺少其开始标记。而且我很迷茫找不到原因。

SSCCE(与初始示例的区别在于包含 <thing><thing_list>):

use XML::Twig;
use IO::Tee;
use feature 'say';

open my $frufile, '>', 'fruit.xml' or die "fruit $!";
open my $vegfile, '>', 'veg.xml' or die "veg $!";

my $tee = IO::Tee->new($frufile, $vegfile);
select $tee;

my $twig=XML::Twig->new(
    twig_handlers => {
        thing  => \&magic,
        _default_  => sub { 
            say STDOUT '_default_ for '.$_->name;
            $_[0]->flush($tee); #default filehandle = tee 
            1; 
        },
    },
    pretty_print => 'indented',
    empty_tags   => 'normal',
);

$twig->parse( *DATA );

sub magic {
    my ($thing, $element) = @_;
    say STDOUT "magic for ". $element->{att}{type};
    for ($element->{att}{type}) {
            if (/fruit/) {
                $thing->flush($frufile);
            } elsif (/vegetable/) {
                $thing->flush($vegfile);
            } else {
                $thing->purge;
            }
    }
    1;
}

__DATA__
<batch>
  <header>
    <foo>1</foo>
    <bar>2</bar>
    <baz>3</baz>
  </header>
  <thing_list>
  <thing type="fruit"     >Im an apple!</thing>
  <thing type="city"      >Toronto</thing>
  <thing type="vegetable" >Im a carrot!</thing>
  <thing type="city"      >Melrose</thing>
  <thing type="vegetable" >Im a potato!</thing>
  <thing type="fruit"     >Im a pear!</thing>
  <thing type="vegetable" >Im a pickle!</thing>
  <thing type="city"      >Patna</thing>
  <thing type="fruit"     >Im a banana!</thing>
  <thing type="vegetable" >Im an eggplant!</thing>
  <thing type="city"      >Taumatawhakatangihangakoauauotamateaturipukakapikimaungahoronukupokaiwhenuakitanatahu</thing>
  </thing_list>
  <trailer>
    <chrzaszcz>A</chrzaszcz>
    <zdzblo>B</zdzblo>
  </trailer>
</batch>

虽然第一个 fruit.xml 没问题:

<batch>
  <header>
    <foo>1</foo>
    <bar>2</bar>
    <baz>3</baz>
  </header>
  <thing_list>
    <thing type="fruit">Im an apple!</thing>
    <thing type="fruit">Im a pear!</thing>
    <thing type="fruit">Im a banana!</thing>
  </thing_list>
  <trailer>
    <chrzaszcz>A</chrzaszcz>
    <zdzblo>B</zdzblo>
  </trailer>
</batch>

veg.xml 缺少 <thing_list>

的开始标记
<batch>
  <header>
    <foo>1</foo>
    <bar>2</bar>
    <baz>3</baz>
  </header>
    <thing type="vegetable">Im a carrot!</thing>
    <thing type="vegetable">Im a potato!</thing>
    <thing type="vegetable">Im a pickle!</thing>
    <thing type="vegetable">Im an eggplant!</thing>
  </thing_list>
  <trailer>
    <chrzaszcz>A</chrzaszcz>
    <zdzblo>B</zdzblo>
  </trailer>
</batch>

我还注意到,如果我在数据中注释掉 <thing_list> 标签,veg.xml 中也缺少对应于开始标签的注释,但 fruit.xml 中没有...

我似乎明白第一个评论是在处理第一个 <thing> 时出现的,第二个应该在处理文件的其余部分时从 _default_ 处理程序中处理。但是我不明白是否相同,而<thing_list>没有评论。

WFIW,我在 Windows 7 box

上使用 Strawberry 的 Perl 5.20.1

哇哦,我很惊讶它的效果如此之好!

第一次到达 $thing->flush($frufile); 时,它会打印之前尚未刷新的所有内容。如果不是您之前尝试修复此问题,它会输出:

<batch>
  <header>
    <foo>1</foo>
    <bar>2</bar>
    <baz>3</baz>
  </header>
  <thing_list>
    <thing type="fruit">Im an apple!</thing>

根据您的尝试,它会打印

  <thing_list>
    <thing type="fruit">Im an apple!</thing>

以后调用magic<thing_list>之前的所有内容都已经打印过了,所以不会再打印了。

不要混合搭配输出句柄!如果要生成两个文件,请对模板进行两次处理。 (并摆脱那个 _default_ 树枝处理程序。)


也就是说,从 twig_handlers 切换到 twig_roots(这对大型文档来说更好)似乎可行:

my $twig = XML::Twig->new(
    twig_roots => {
        'thing_list/thing' => sub {
            my ($t, $element) = @_;
            for ($element->{att}{type}) {
                if (/fruit/) {
                    $t->flush($frufile);
                } elsif (/vegetable/) {
                    $t->flush($vegfile);
                } else {
                    $t->purge;
                }
            }
        },
    },
    twig_print_outside_roots => 1,
    pretty_print => 'indented',
    empty_tags => 'normal',
);

使用风险自负:)