2016-05-17 4 views
2

Попытка узнать XML::Twig и получить некоторые данные из документа XML.Как пропустить нежелательные элементы с помощью XML :: Twig?

Мой XML содержит 20k + <ADN> элементов. Элемент Eaach <ADN> содержит десятки дочерних элементов, один из которых - <GID>. Я хочу, чтобы процесс только те ADN где GID == 1. (См примера XML является __DATA__)

Документы говорят:

Обработчиков вызываются в установленном порядке, отсортированный по их типу (XPATH сначала выражения, затем регулярные выражения, затем уровень), а затем: указать полный путь (начиная с корневого элемента) или нет, затем по количество шагов в выражении, затем число предикатов, затем количество тестов в предикаты. Обработчики, где последний шаг не указан , указывают шаг (foo/bar/*), запускаемый после других обработчиков XPath. Наконец все обработчики запускаются последним.

Важно: как только обработчик был вызван, если она возвращает 0, то нет другого обработчика не вызывается, кроме всех обработчика, который будет называться в любом случае.

Мой реальный код:

use 5.014; 
use warnings; 
use XML::Twig; 
use Data::Dumper; 

my $cat = load_xml_catalog(); 
say Dumper $cat; 

sub load_xml_catalog { 
     my $hr; 
     my $current; 
     my $twig= XML::Twig->new(
     twig_roots => { 
      ADN => sub {  # process the <ADN> elements 
       $_->purge; # and purge when finishes with one 
      }, 
     }, 
     twig_handlers => { 
      'ADN/GID' => sub { 
       return 1 if $_->trimmed_text == 1; 
       return 0;  # skip the other handlers - if the GID != 1 
      }, 

      'ADN/ID' => sub { #remember the ID as a "key" into the '$hr' for the "current" ADN 
       $current = $_->trimmed_text; 
       $hr->{$current}{$_->tag} = $_->trimmed_text; 
      }, 

      #rules for the wanted data extracting & storing to $hr->{$current} 
      'ADN/Name' => sub { 
       $hr->{$current}{$_->tag} = $_->text; 
      }, 
     }, 
     ); 
     $twig->parse(\*DATA); 
    return $hr; 
} 
__DATA__ 
<ArrayOfADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1</ID> 
     <Name>name 1</Name> 
    </ADN> 
    <ADN> 
     <GID>2</GID> 
     <ID>20</ID> 
     <Name>should be skipped because GID != 1</Name> 
    </ADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1000</ID> 
     <Name>other name 1000</Name> 
    </ADN> 
</ArrayOfADN> 

Он выводит

$VAR1 = { 
      '1000' => { 
        'ID' => '1000', 
        'Name' => 'other name 1000' 
        }, 
      '1' => { 
       'Name' => 'name 1', 
       'ID' => '1' 
       }, 
      '20' => { 
        'Name' => 'should be skipped because GID != 1', 
        'ID' => '20' 
       } 
     }; 

Так,

  • Обработчик для ADN/GID возвращает 0 когда GID = 1.
  • !
  • Почему другие обработчики еще вызываются?
  • Ожидаемый (желаемый) выход без '20' => ....
  • Как пропустить нежелательные узлы правильно?
+0

@toolic Согласовывающ документ, _Handlers запускаются в фиксированном order_, так что, вероятно, я не понимаю, как определяется порядок - aka, как решить вышеперечисленное ...;) – cajwine

+0

Проблема в том, что триггер ручного управления в фиксированном порядке _ для определенного элемента_. Они перезагружаются для каждого отдельного. – Sobrique

ответ

4

В этом контексте вещь «возвращает ноль» - это немного красная селедка. Если у вас было несколько совпадений на вашем элементе, то один из из них, возвращающий ноль, заблокировал остальные.

Это не значит, что он еще не попытается обработать последующие узлы.

Я думаю, что вы запутались - у вас есть обработчики для отдельных подэлементов ваших элементов <ADN> - и они запускаются отдельно. Это по дизайну. Существует порядок приоритета для xpath, но только в двух экземплярах. Твои полностью разделены, поэтому они все «стреляют», потому что они срабатывают по разным элементам.

Однако, вы могли бы найти это полезно знать - twig_handlers позволяет xpath выражения - так что вы можете явно сказать:

#!/usr/bin/env perl 
use strict; 
use warnings; 

use XML::Twig; 
my $twig = XML::Twig->parse(\*DATA); 
$twig -> set_pretty_print('indented_a'); 

foreach my $ADN ($twig -> findnodes('//ADN/GID[string()="1"]/..')) { 
    $ADN -> print; 
} 

Это также работает в синтаксисе twig_handlers. Я бы посоветовал сделать обработчик только очень полезным, если вам нужно предварительно обработать ваш XML, или вы ограничены памятью. С 20 000 узлов вы можете быть. (в этот момент purge - твой друг).

#!/usr/bin/env perl 
use strict; 
use warnings; 

use XML::Twig; 
my $twig = XML::Twig->new(
    pretty_print => 'indented_a', 
    twig_handlers => { 
     '//ADN[string(GID)="1"]' => sub { $_->print } 
    } 
); 

$twig->parse(\*DATA); 


__DATA__ 
<ArrayOfADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1</ID> 
     <Name>name 1</Name> 
    </ADN> 
    <ADN> 
     <GID>2</GID> 
     <ID>20</ID> 
     <Name>should be skipped because GID != 1</Name> 
    </ADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1000</ID> 
     <Name>other name 1000</Name> 
    </ADN> 
</ArrayOfADN> 

Хотя, я бы, вероятно, просто сделать это таким образом, вместо:

#!/usr/bin/env perl 
use strict; 
use warnings; 

use XML::Twig; 

sub process_ADN { 
    my ($twig, $ADN) = @_; 
    return unless $ADN -> first_child_text('GID') == 1; 
    print "ADN with name:", $ADN -> first_child_text('Name')," Found\n"; 
} 


my $twig = XML::Twig->new(
    pretty_print => 'indented_a', 
    twig_handlers => { 
     'ADN' => \&process_ADN 
    } 
); 

$twig->parse(\*DATA); 


__DATA__ 
<ArrayOfADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1</ID> 
     <Name>name 1</Name> 
    </ADN> 
    <ADN> 
     <GID>2</GID> 
     <ID>20</ID> 
     <Name>should be skipped because GID != 1</Name> 
    </ADN> 
    <ADN> 
     <GID>1</GID> 
     <ID>1000</ID> 
     <Name>other name 1000</Name> 
    </ADN> 
</ArrayOfADN> 

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

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