LibXML findnodes($query)

У меня возникли проблемы с этим кодом:

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;

В частности, "print $ node" печатает пустую строку, даже если путь к XML-файлу указан правильно и запрос XPath должен вернуть узел.

Самое смешное, что если я использую:

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

я получил правильный результат.

Это файл news.xml:

<?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 ответ

Решение

Ваш входной XML-документ находится в пространстве имен по умолчанию:

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

Этот элемент и все его потомки находятся в этом пространстве имен, и выражение как //notizie никогда не будет успешным, потому что он ищет элемент без пространства имен.

С другой стороны, вот почему /*/* возвращает элементы - потому что * соответствует элементам в любом (или нет) пространстве имен. В этом нет ничего смешного.

Либо объявите это пространство имен в своем коде Perl (лучший вариант), либо игнорируйте пространства имен в выражении XPath.

Объявление пространства имен с помощью LibXML

Я считаю, что объявление пространств имен в LibXML выполняется с registerNs()см. соответствующую страницу CPAN. Объявите URI пространства имен из входного XML вместе с префиксом (news:(в этом примере), который затем можно использовать для определения имен элементов в выражении XPath.

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);

Игнорирование пространств имен

Второй вариант означает изменение выражения XPath на

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

Вышеупомянутое выражение найдет notizie элемент во всех следующих документах:

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

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


<!--Default namespace-->
<notizie xmlns="http://www.9armonie.com/news"/>
Другие вопросы по тегам