2015-04-24 3 views
3

Я немного XML, который выглядит следующим образом:С XML :: Twig, есть ли способ найти «first_child» с определенным атрибутом?

<?xml version="1.0" encoding="UTF-8"?> 
<DataSet> 
<Category> 
    <Name mode="source">somename</Name> 
    <Name mode="destination">someothername</Name> 
    <Content>Some text here</Content> 
</Category> 
</DataSet> 

То, что я пытаюсь сделать, это процесс «категория», и извлечь другое имя на основе контекста.

Я попытался перебор его children - это работает:

use strict; 
use warnings; 
use XML::Twig; 

sub process_category { 
    my ($twig, $category) = @_; 
    my $cat_name; 
    foreach my $name ($category->children('Name')) { 
     if ($name->att('mode') eq 'source') { 
      $cat_name = $name->text; 
     } 
    } 

    print "$cat_name ", $category->first_child_text('Content'), "\n"; 
} 

my $twig = 
    XML::Twig->new(twig_handlers => { 'Category' => \&process_category }) 
    ->parse(\*DATA); 


__DATA__ 
<?xml version="1.0" encoding="UTF-8"?> 
<DataSet> 
<Category> 
    <Name mode="source">somename</Name> 
    <Name mode="destination">someothername</Name> 
    <Content>Some Text</Content> 
</Category> 
</DataSet> 

Однако мне интересно, - есть лучший способ, чем итерация элементы? Я не могу понять, поддерживает ли first_child поиск атрибута, или если есть другой метод, который делает то же самое.

ответ

4

Используйте метод XML :: Twig get_xpath для поиска совпадающих значений в атрибутах. Например:

my $cat_name = $category->get_xpath('./Name[@mode="source"]', 0)->text; 

По умолчанию get_xpath возвращает массив. Передавая «0», передается только первый элемент массива (это то, что вам нужно, и, скорее всего, будет только одно совпадение). Затем текст вытягивается с ->text. Используйте это, и вы можете удалить цикл for.

+0

Lovely, это звучит даже лучше, чем решение для обработки кода, на которое я смотрел. – Sobrique

0

Вы можете передать код_ref в first_child. Этот sub передается каждому элементу по очереди, и если он возвращает «true», то метод first_child соответствует. (И затем не продолжает искать).

Так что это следует сделать трюк:

use strict; 
use warnings; 
use XML::Twig; 

sub is_name_source { 
    my ($element) = @_; 

    print $element ->tag, "\n"; 
    if ( $element->tag eq 'Name' 
     and $element->att('mode') eq 'source') 
    { 
     return 1; 
    } 
} 

sub process_category { 
    my ($twig, $category) = @_; 
    my $cat_name = $category->first_child(\&is_name_source)->text; 
    print "$cat_name ", $category->first_child_text('Content'), "\n"; 
} 

my $twig = 
    XML::Twig->new(twig_handlers => { 'Category' => \&process_category }) 
    ->parse(\*DATA); 


__DATA__ 
<?xml version="1.0" encoding="UTF-8"?> 
<DataSet> 
<Category> 
    <Name mode="source">somename</Name> 
    <Name mode="destination">someothername</Name> 
    <Content>Some Text</Content> 
</Category> 
</DataSet> 

Вы могли бы, конечно, рядный is_name_source с анонимным суб. Это вопрос вкуса.