【问题标题】:Buggy output from XML::Twig on a TeeXML::Twig 在 Tee 上的错误输出
【发布时间】:2016-02-25 13:39:17
【问题描述】:

我正在尝试将 xml 文件拆分为多个格式正确的片段,an ancient PerlMonks solution 在 XML::Twig 的帮助下做得很好数据输入。

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

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 缺少&lt;thing_list&gt; 的开始标签

<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>

我还注意到,如果我将&lt;thing_list&gt;标签注释到数据中,则veg.xml中也缺少与开始标签相对应的注释,但fruit.xml中没有...

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

WFIW,我在 Windows 7 机器上使用 Strawberry 的 Perl 5.20.1

【问题讨论】:

  • 可以在 Linux 上重现
  • @ikegami 你的意思是“不能复制”?
  • 不,没有错字。我写的是我的意思。
  • @ikegami 感谢您花时间进行测试。所以它要么坏了,要么我更可能误解了 XML::Twig 的用法

标签: xml perl xml-parsing xml-twig


【解决方案1】:

哇哦,我很惊讶它的效果和它一样好!

当您第一次到达$thing-&gt;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&lt;thing_list&gt; 以及之前的所有内容都已打印,因此不会再次打印。

不要混合和匹配输出句柄!如果要生成两个文件,请处理模板两次。 (并摆脱那个_default_ twig 处理程序。)


也就是说,从 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',
);

使用风险自负:)

【讨论】:

  • 所以我对 XML::Twig 的理解需要修复。 &lt;thing_list&gt; 是否应该以第一个 &lt;thing&gt; 作为其父级?如果是这样,我不明白为什么对magic 处理程序的所有后续调用都不是这种情况。
  • 不,您的程序需要修复。不要混合和匹配输出句柄。如果需要生成两个文件,请运行模板两次。
  • 哦,太糟糕了。我的实际情况是将一个可能很大的文件拆分为“较小”的块(30..200MB),所以我发现使用 Tee 在多个文件中一次输出页眉和页脚部分并使用处理程序调度其余部分的技巧是无需多次读取文件的巧妙解决方案。我可能会尝试使用基于 SAX 的解析。
  • @Seki,更新了我的答案。
  • 感谢您的更新。现在它非常接近我想要得到的,但它正在生成稀疏文件:有一些空行只有与丢弃的项目相对应的缩进,即使是pretty_print =&gt; 'none'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
  • 2018-02-17
  • 2015-10-01
  • 2011-07-03
  • 1970-01-01
  • 2023-04-05
相关资源
最近更新 更多