Perl XML получает дочерние данные того же родителя - PullRequest
2 голосов
/ 22 марта 2019

У меня есть следующий XML, и я надеюсь получить дочерний элемент от того же родителя, если регулярное выражение соответствует другому дочернему элементу. Проблема в том, что в XML повсюду дубликаты тегов именования, поэтому сложно просто выполнить «Кино-> год», потому что в нем много элементов фильма.

, например

Данные:

<movie>
    <title>Titanic</title>
    <year>1997</year>
    <genre>Drama</genre>
</movie>
<movie>
    <title>Moneyball</title>
    <year>2011</year>
    <genre>Sport/Drama</genre>
</movie>
<movie>
    <title>Fight Club</title>
    <year>1999</year>
    <genre>Drama/Action</genre>
</movie>

Perl

 my $simple = XML::Simple->new( );
 my $tree = $simple->XMLin($_);
 my $movie = $tree->{movie}{title};

if($movie =~ /Titanic/)
{
    # $movie -> year ???
    # desired output = 1997
}

Какой самый простой способ сделать это с XML :: Simple?

Ответы [ 4 ]

4 голосов
/ 22 марта 2019

Нет простого способа с XML :: Simple, потому что это самый сложный анализатор XML для использования . Его собственная документация предупреждает против его использования. («Использование этого модуля в новом коде настоятельно не рекомендуется .»)


То, что у вас есть, не является допустимым XML, поэтому сначала нам нужно сделать его действительным XML

use XML::LibXML qw( );

my $parser = XML::LibXML->new();
my $doc = $parser->parse_string("<movies>$not_quite_xml</movies>");

my ($movie_node) = $doc->findnodes('/movies/movie[title/text()="Titanic"]')
   or die("Titanic not found\n");

my $year = $movie_node->findvalue('year/text()');
...
3 голосов
/ 22 марта 2019

Я надеюсь, что было передано, что XML::Simple не должен использоваться, поскольку давно заменен гораздо лучшими модулями и "категорически не рекомендуется" против его собственного автора, также много лет назад.

В этом примере показан способ использования родительского узла для запроса братьев и сестер, как это специально просили.(Я дополняю ваш образец корневым узлом <document>, чтобы получить правильно сформированный XML.) Ответ ikegami показывает, как вы можете более прямо делать то, что вам нужно.

Если у вас есть причинадля сканирования через узлы <title> (возможно, для поиска различных названий), их <year> узлы могут быть найдены с помощью

use strict;
use warnings;
use feature 'say';    

use XML::LibXML;    

my $file = shift || die "Usage: $0 filename\n";

my $doc = XML::LibXML->load_xml(location => $file, no_blanks => 1); 

my $xpath = '/document/movie/title';

foreach my $node ($doc->findnodes($xpath)) {
    if ($node->to_literal =~ /(Titanic)/) {
        say "Title: $1";
        foreach my $yr ($node->parentNode->findnodes('./year')) {
            say "\tyear: ", $yr->to_literal;
        }   
    }   
}

Если под одним узлом <year> всегда есть один узелУзел <movie>, тогда это можно упростить с помощью ярлыка findvalue, заменив цикл на $node->parentNode->findnodes, для

foreach my $node ($doc->findnodes($xpath)) {
    if ($node->to_literal =~ /(Titanic)/) {
        say "Title: $1";
        say "\tyear: ", $node->parentNode->findvalue('./year');
    }   
}

Здесь мы получаем текст напрямую и поэтому нет необходимости в ->to_literalЛибо.

В XML :: LibXML :: Node , базовом классе для узлов, используемых для получения других конкретных классов, есть еще много методов.Один из интересующих здесь может быть nextSibling, как способ просмотра другой информации о названии в пределах одного <movie>.

Обратите внимание, что эта полная и полнофункциональная библиотека предоставляет гораздо больше инструментов для работы с XML.Например, добавление деталей в ваш исходный файл, например, атрибутов, позволит использовать другие сильные стороны библиотеки.

Документация распространяется на несколько страниц.См. в этом посте для краткого изложения ссылок на соответствующие документы.Существует также учебник по XML :: LibXML , автором XML::Simple.

2 голосов
/ 01 апреля 2019

Еще один способ сделать это: Mojo :: DOM на этот раз.Там нет ничего, чтобы рекомендовать это по сравнению с другими решениями (кроме XML :: Simple).

Это добавляет корневой элемент, а затем использует селектор CSS для получения заголовков:

use utf8;
use strict;
use warnings;

my $xml = <<'HERE';
<movies>
<movie>
    <title>Titanic</title>
    <year>1997</year>
    <genre>Drama</genre>
</movie>
<movie>
    <title>Moneyball</title>
    <year>2011</year>
    <genre>Sport/Drama</genre>
</movie>
<movie>
    <title>Fight Club</title>
    <year>1999</year>
    <genre>Drama/Action</genre>
</movie>
</movies>
HERE

use Mojo::DOM;

my @movies = Mojo::DOM
    ->new( $xml )
    ->find( 'movies title' )
    ->map( 'text' )
    ->each;

say join "\n", @movies;
1 голос
/ 22 марта 2019

Вы также можете вызвать инструмент командной строки, такой как xmlstarlet, из Perl, чтобы быстро извлечь только ту информацию, которая вам нужна.

Например, если ваш фрагмент документа XML был сохранен в /tmp/foo.xml, тогдаследующий скрипт оболочки преобразует его в табличную форму, которую легче обрабатывать в Perl, читая строку за раз.

{ echo '<movies>' ; cat /tmp/foo.xml ; echo '</movies>'; } \
    | xmlstarlet sel -T -t -m '//movie' -v "concat(title, '|', year)" -n

print

Titanic|1997
Moneyball|2011
Fight Club|1999

Этот особый способ конвертацииXML-документ в более удобной форме не является устойчивым к символам новой строки или | s в заголовках фильмов и требует внешнего инструмента, но это просто.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...