存在xmlns时,XML :: LibXML findnodes()不返回结果

时间:2018-10-20 12:37:38

标签: perl xml-libxml

我正在使用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 :(得分:4)

您要求查找名称空间为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);

答案 1 :(得分:1)

您的代码首先使用XML::LibXML::Reader API,然后使用XML::LibXML->load_xml从文档的一部分创建DOM。 XML::LibXML::Reader API通常仅与大型XML文档一起使用,这些XML文档作为DOM加载时会消耗大量内存。如果您的XML文档不是很大,那么使用ikegami's answer之类的方法要简单得多,该方法仅使用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来使用支持名称空间的XPath语句来查询DOM。

我的XML :: LibXML教程涵盖了working with XML namespacesworking with large documents的内容。