В следующем коде я ожидал, что он выберет каждый узел, но он выберет только каждый второй узел. Это правильное поведение или ошибка?
<?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)
Это похоже на ошибку в 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, вы на самом деле не используете пространства имен для своих узлов, а только для атрибута (ов).
Прежде всего, ни один из элементов — это элементы тот //*
выбирает — в вашем примере XML находятся в пространстве имен.
Во-вторых, реализация оси пространства имен в XPath 1.0 часто является дрянной. Не удивляйтесь тому, что он не работает должным образом, так как его здесь нет.
Для лучших результатов используйте этот XPath для выбора элементов в foo
Пространство имен:
"//*[namespace-uri() = 'foo']"
Хотя, как уже упоминалось, в foo
пространство имен в вашем примере; есть только атрибут. Чтобы выбрать все атрибуты в foo
пространство имен (в данном случае одно), используйте следующий XPath:
"//@*[namespace-uri() = 'foo']"
Обновить:
Я думаю, что вижу замешательство. Я не ищу узлы в этом
пространство имен, но узлы, в которых пространство имен доступно (то есть для других
узлы).
Под «доступным», возможно, вы подразумеваете объявленный? В этом случае у вас есть две проблемы:
Для получения дополнительной информации см. Найти все объявления пространства имен в документе XML — xPath 1.0 против xPath 2.0.