XML :: LibXML findnodes () не возвращает результаты при наличии xmlns - PullRequest
0 голосов
/ 20 октября 2018

Я использую XML :: LibXML :: Reader для анализа большого документа и столкнулся с проблемой, из-за которой атрибут xmlns приводит к сбою findnodes ().Я исправил это, добавив регулярное выражение для удаления атрибута xmls, но мне было интересно, есть ли более элегантное решение без использования регулярных выражений.Если вы удалите строку регулярного выражения ($ xml = ~ s {xmlns ...), вы увидите, что «Loc = $ loc» не дает результатов.

Вот код:

use strict;
use warnings;
use feature qw( say );
use XML::LibXML::Reader qw( XML_READER_TYPE_ELEMENT );

my $xml = <<'__EOI__';
<url xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <loc>http://example.com</loc>
    <lastmod>2018-10-19</lastmod>
</url>
__EOI__


$xml =~ s{xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"}{};

my $reader = XML::LibXML::Reader->new( string => $xml);
while ( $reader->read ) {
    next unless $reader->nodeType == XML_READER_TYPE_ELEMENT;
    next unless $reader->name eq 'url';
    my $xml = $reader->readOuterXml;
    my $doc = XML::LibXML->load_xml(string => $xml);
    say "Doc = $doc";
    my ($loc) = $doc->findnodes('//loc');
    say "Loc = $loc";
}

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Ваш код начинается с использования XML::LibXML::Reader API, а затем позже использует XML::LibXML->load_xml для создания DOM из части документа.API XML::LibXML::Reader обычно используется только с большими XML-документами, которые занимают большие объемы памяти при загрузке в качестве DOM.Если ваш XML-документ не очень большой, то гораздо проще использовать такой подход, как ответ ikegami , который просто использует DOM API для загрузки всего документа, а затем запрашивает его с помощью XPath.

Однако, если у вас действительно есть огромный XML-документ, то вам может быть интересно решить проблему с помощью Reader API:

my $sitemap_uri = 'http://www.sitemaps.org/schemas/sitemap/0.9';
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs(sm => $sitemap_uri);

my $reader = XML::LibXML::Reader->new(location => './sitemap.xml');
while ($reader->read) {
    $reader->nextElement('url', $sitemap_uri) or last;
    my $doc = $reader->copyCurrentNode(1);
    say "Doc = $doc";
    my ($loc) = $xpc->findnodes('//sm:loc', $doc);
    say "Loc = $loc";
}

Вызов $reader->nextElement - это быстрый способ перейти к следующему вхождению.определенного элемента.В этом примере я сопоставил и имя элемента, и его пространство имен.

Вызов $reader->copyCurrentNode(1) - это удобный метод, который возвращает этот узел и все его дочерние узлы в виде фрагмента DOM.Вам нужно будет использовать XML::LibXML::XPathContext для запроса этого DOM с помощью операторов XPath с учетом пространства имен.

В моем учебнике по XML :: LibXML содержится охват работы с пространствами имен XML , а также работа с большими документами .

0 голосов
/ 20 октября 2018

Вы просите найти узлы с пространством имен null и с именем loc.В документе нет таких узлов, поэтому findnodes правильно ничего не возвращает.

Вы хотите найти узлы с пространством имен http://www.sitemaps.org/schemas/sitemap/0.9 и именем loc.Для этого вы можете использовать следующее:

my $doc = XML::LibXML->load_xml( string => $xml );

my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( sm => 'http://www.sitemaps.org/schemas/sitemap/0.9' );

my ($loc) = $xpc->findnodes('//sm:loc', $doc);
...