XPath-фильтрация атрибута с пространством имен

Мне нужно создать выражение XPath для фильтрации на основе атрибута, который находится в заданном пространстве имен. Пример XML:

<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'
  xmlns:yt='http://gdata.youtube.com/schemas/2007'> ...
 <entry>
  <media:group>
   <media:thumbnail url='http://i.ytimg.com/1.jpg' yt:name='default'/>
   <media:thumbnail url='http://i.ytimg.com/2.jpg' yt:name='hqdefault'/>
   <media:thumbnail url='http://i.ytimg.com/3.jpg' yt:name='start'/>
   <media:thumbnail url='http://i.ytimg.com/4.jpg' yt:name='middle'/>
  </media:group>
 </entry>

И мне нужно получить URL-адрес node с атрибутом yt: name, установленным в 'hqdefault'.

Я попробовал с выражением XPath

'./media:group/media:thumbnail[@yt:name='hqdefault']/@url'

но кажется, что указание атрибута namespaced с именем yt: name не работает. При создании запроса я получаю пустой DOMNodeList.

Я обращаюсь к XML в php, поэтому я зарегистрировал пространство имен yt:

registerNamespace( 'yt', 'http://gdata.youtube.com/schemas/2007' );

Thnx заранее

Ответ 1

Что XPath выглядит правильно.

Возможно, ваша библиотека не поддерживает атрибуты с расширением имен или что вы неправильно зарегистрировали пространство имен yt и/или пространство имен media.

Попробуйте просто совместить на local-name() и namespace-uri() внутри предикатных фильтров, вместо использования префикса пространства имен:

./*[local-name()='group'
      and namespace-uri()='http://search.yahoo.com/mrss/'
    ]/*[local-name()='thumbnail'
         and namespace-uri()='http://search.yahoo.com/mrss/'
         and @*[local-name()='name'
                 and namespace-uri()='http://gdata.youtube.com/schemas/2007'
                 and .='hqdefault'
                ]
        ]/@url

Если это работает, тогда возникает проблема регистрации пространств имен для этих префиксов пространства имен.

Ответ 2

Предполагая, что все остальное в порядке, просто замените первый . в xpath на /, чтобы получить //media:group/... (или начать с /atom:feed/media:group/... и зарегистрировать пространство имен атомов).

Вот полный рабочий пример:

<?php
$dom = new DOMDocument();
$dom->loadXML( <<<XML
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'
  xmlns:yt='http://gdata.youtube.com/schemas/2007'>
  <entry>
    <media:group>
      <media:thumbnail url='http://i.ytimg.com/1.jpg' yt:name='default'/>
      <media:thumbnail url='http://i.ytimg.com/2.jpg' yt:name='hqdefault'/>
      <media:thumbnail url='http://i.ytimg.com/3.jpg' yt:name='start'/>
      <media:thumbnail url='http://i.ytimg.com/4.jpg' yt:name='middle'/>
    </media:group>
  </entry>
</feed>
XML
);

$x = new DOMXPath( $dom );
$x->registerNamespace( 'yt', 'http://gdata.youtube.com/schemas/2007' );
$x->registerNamespace( 'media', 'http://search.yahoo.com/mrss/' );
$l= $x->query( "//media:group/media:thumbnail[@yt:name='hqdefault']/@url" );
for ($i=0; $i<$l->length; $i++) var_dump( $l->item($i)->value );

Ответ 3

Префикс пространства yt используется в вашем примере XML, но не объявлен. Если этот пример XML действительно есть все, это не правильно сформированный XML (в терминах пространства имен). Поэтому никакие общие инструменты XML, такие как XSLT, скорее всего, не смогут его обработать.

С другой стороны, если есть объявление префикса пространства имен yt где-то в исходном документе, который вы нам не показали, вам нужно объявить в своей рабочей среде XPath (XSLT, я думаю) префикс для один и тот же URI пространства имен. Например.

<xsl:stylesheet ... xmlns:yt="theNamespaceURIForYT">

XPath не знает о каких-либо объявлениях префикса пространства имен, которые встречаются в исходном документе. Он знает только, какое пространство имен (URI) принадлежит каждому элементу (и атрибуту).