Глючный вывод из 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',
);
Используйте на свой риск:)