Глючный вывод из XML::Twig на тройнике

Я пытаюсь разбить xml-файл на несколько правильно сформированных фрагментов, и древнее решение PerlMonks делает довольно хорошо то, что я ищу, с помощью XML::Twig, который сплетается в тройник... по крайней мере, с простым вводом данных,

Если я немного усложню структуру данных, перегруппировав узлы для фильтрации в родительский узел, второй файл сформирован неправильно: у родительского узла отсутствует открывающий тег. И я совершенно потерян, чтобы найти причину.

SSCCE (разница с первоначальным примером заключается в <thing_list> который содержит <thing>"S):

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, я использую Perl 5.20.1 от Strawberry на Windows 7

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',
);

Используйте на свой риск:)

Другие вопросы по тегам