LibXML findnodes($query)

2019-09-17 06:00发布

I'm having some trouble with this code:

my $file= '../xml/news.xml';
my $parser= XML::LibXML->new();
my $doc = $parser->parse_file($file);
my $xpc = XML::LibXML::XPathContext->new($doc);
my $query = '/notizie/news[@id='.$newsId.']';
print $query;
my $node = $xpc->findnodes($query)->get_node(1);

print $node;

In particular "print $node" prints an empty string, even if the XML file path is correct and the XPath query should return a node.

The "funny" thing is that if i use:

my $query = '/*/*[@id='.$newsId.']'; 

i got the right result.

This is the news.xml file:

<?xml version="1.0"?>
<notizie xmlns="http://www.9armonie.com/news"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.9armonie.com/news news.xsd">
    <news id="3">
        <data>2015-01-01</data>
        <ora>12:00:00</ora>
        <titolo>Title 3</titolo>
        <descrizione> Description 3</descrizione>
    </news>     
    <news id="2">
        <data>2014-12-19</data>
        <ora>12:00:00</ora>
        <titolo>Title 2</titolo>
        <descrizione> Description 2</descrizione>
    </news>
    <news id="1">
        <data>2014-12-18</data>
        <ora>12:00:00</ora>
        <titolo>News 1</titolo>
        <descrizione> Desc 1</descrizione>
    </news>
    <news id="0">
        <data>2014-12-18</data>
        <ora>12:00:00</ora>
        <titolo> asdasd</titolo>
        <descrizione> First! </descrizione>
    </news>
</notizie>

1条回答
爷的心禁止访问
2楼-- · 2019-09-17 06:45

Your input XML document is in a default namespace:

<notizie xmlns="http://www.9armonie.com/news"/>

This element and all its descendants are in that namespace, and an expression like //notizie will never be successful because it looks for an element without a namespace.

On the other hand, that's also why /*/* returns the elements - because * matches elements in any (or no) namespace. There is nothing funny about it really.

Either declare this namespace in your Perl code (the better option), or ignore namespaces in your XPath expression.

Declaring the namespace with LibXML

I believe declaring namespaces in LibXML is done with registerNs(), see the relevant CPAN page. Declare the namespace URI from the input XML together with a prefix (news:, in this example) that you can then use to qualify the element names in the XPath expression.

my $xpc = XML::LibXML::XPathContext->new($doc);
$xpc->registerNs('news', 'http://www.9armonie.com/news');
my $query = '/news:notizie/news:news[@id='.$newsId.']';
my $node = $xpc->findnodes($query)->get_node(1);

Ignoring namespaces

The second option means modifying your XPath expression to

"/*[local-name() = 'notizie']/*[local-name() = 'news' and @id='.$newsId.']"

The above expression will find the notizie element in all of the following documents:

<!--No namespace-->
<notizie/>

<!--Namespace with prefix-->
<news:notizie xmlns:news="http://www.9armonie.com/news"/>


<!--Default namespace-->
<notizie xmlns="http://www.9armonie.com/news"/>
查看更多
登录 后发表回答