2016-02-25 3 views
1

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

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

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

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 Клубничный на Windows 7 в коробке

+2

Может воспроизводить на Linux – ikegami

+0

@ikegami, который вы имели в виду «не может воспроизвести»? – Seki

+0

Нет, нет опечаток. Я написал то, что имел в виду. – ikegami

ответ

3

Ого, я удивлен, что работает так же как это делает!

В первый раз, когда вы достигаете $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', 
); 

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

+0

Так что я понимаю, что XML :: Twig необходимо исправлять. Предполагается, что '' должен быть указан с первым '' как его родителем? Если это так, я не понимаю, почему это не относится ко всем следующим вызовам обработчика 'magic'. – Seki

+1

Нет, ваша программа должна быть исправлена. Не смешивайте и не совместите выходные ручки. Если вам нужно сгенерировать два файла, дважды запустите шаблон. – ikegami

+0

О, слишком плохо. Мой фактический случай состоит в том, чтобы разделить на «мелкие» куски (30..200MB) файл, который может быть огромным, поэтому я обнаружил, что трюк для вывода частей заголовка и нижнего колонтитула сразу в нескольких файлах с помощью Tee и отправки остальным с обработчиками умное решение для того, чтобы не читать файл несколько раз. Вероятно, я попытаюсь использовать синтаксический анализ на основе SAX. – Seki

 Смежные вопросы

  • Нет связанных вопросов^_^