XPath поиск пространств имен выбирает каждый второй узел

В следующем коде я ожидал, что он выберет каждый узел, но он выберет только каждый второй узел. Это правильное поведение или ошибка?

<?php
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadXML('<root xmlns:ns="foo" ns:id="1"><one/><two/><three/><four/><five/></root>');
$finder = new DOMXPath($doc);
$nodes = $finder->query("//*[namespace::ns]");
foreach ($nodes as $n) {
var_dump($doc->saveXML($n, LIBXML_NOEMPTYTAG));
}
?>

Выход:

string '<root xmlns:ns="foo" ns:id="1"><one></one><two></two><three></three><four></four><five></five></root>' (length=101)
string '<two></two>' (length=11)
string '<four></four>' (length=13)

2

Решение

Это похоже на ошибку в libxml, используемом PHP. Это приводит как имя в корне, два, четыре.

Это становится еще страннее при использовании //*[namespace::*], тогда вы получите корневой узел и узлы один, три, пять.

Хотя пространства имен наследуются всеми дочерними узлами, похоже, что в этом есть некоторая ошибочная реализация.


Использование следующего xslt создает все желаемые узлы:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<matches>
<xsl:copy-of select="//*[namespace::ns]"/>
</matches>
</xsl:template>
</xsl:stylesheet>

Сам XPath можно изменить на // *, чтобы выбрать все узлы — независимо от пространства имен. Также, как указал kjhughes, вы на самом деле не используете пространства имен для своих узлов, а только для атрибута (ов).

0

Другие решения

Прежде всего, ни один из элементов — это элементы тот //* выбирает — в вашем примере XML находятся в пространстве имен.

Во-вторых, реализация оси пространства имен в XPath 1.0 часто является дрянной. Не удивляйтесь тому, что он не работает должным образом, так как его здесь нет.

Для лучших результатов используйте этот XPath для выбора элементов в foo Пространство имен:

"//*[namespace-uri() = 'foo']"

Хотя, как уже упоминалось, в foo пространство имен в вашем примере; есть только атрибут. Чтобы выбрать все атрибуты в foo пространство имен (в данном случае одно), используйте следующий XPath:

"//@*[namespace-uri() = 'foo']"

Обновить:

Я думаю, что вижу замешательство. Я не ищу узлы в этом
пространство имен, но узлы, в которых пространство имен доступно (то есть для других
узлы).

Под «доступным», возможно, вы подразумеваете объявленный? В этом случае у вас есть две проблемы:

  1. Как упоминалось выше, реализация оси пространства имен часто
    слабый.
  2. На уровне модели данных существуют эквивалентности между документами XML
    которые объявляют пространства имен на разных узлах, так что
    местоположение декларации может быть неясно.

Для получения дополнительной информации см. Найти все объявления пространства имен в документе XML — xPath 1.0 против xPath 2.0.

0

По вопросам рекламы [email protected]