Используйте XPath с PHP SimpleXML для поиска узлов, содержащих строку

Я пытаюсь использовать SimpleXML в сочетании с XPath для поиска узлов, содержащих определенную строку.

<?php
$xhtml = <<<EOC
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <title>Test</title>
    </head>
    <body>
        <p>Find me!</p>
        <p>
            <br />
            Find me!
            <br />
        </p>
    </body>
</html>
EOC;

$xml = simplexml_load_string($xhtml);
$xml->registerXPathNamespace('xhtml', 'http://www.w3.org/1999/xhtml');

$nodes = $xml->xpath("//*[contains(text(), 'Find me')]");

echo count($nodes);

Ожидаемый результат: 2 Фактический результат: 1

Когда я меняю xhtml второго абзаца на

<p>
    Find me!
    <br />
 </p>

тогда он работает, как ожидалось. Как должно выглядеть мое выражение XPath, чтобы оно соответствовало всем узлам, содержащим «Найди меня», независимо от того, где они находятся?

Использование PHP DOM-XML возможно, но нежелательно.

Заранее спасибо!


person xlttj    schedule 16.09.2010    source источник


Ответы (3)


Это зависит от того, что вы хотите сделать. Вы можете выбрать все элементы <p/>, которые содержат «Найди меня» в любом из их потомков с помощью

//xhtml:p[contains(., 'Find me')]

Это вернет дубликаты, и поэтому вы не укажете тип узлов, тогда он также вернет <body/> и <html/>.

Или, возможно, вам нужен любой узел, у которого есть дочерний (не потомок) текстовый узел, содержащий «Найди меня».

//*[text()[contains(., 'Find me')]]

Этот не вернет <html/> или <body/>.


Я забыл упомянуть, что . представляет собой все текстовое содержимое узла. text() используется для получения [набора узлов] текстовых узлов. Проблема с вашим выражением contains(text(), 'Find me') заключается в том, что contains() работает только со строками, а не с наборами узлов, и поэтому оно преобразует text() в значение первого узла, поэтому удаление первого <br/> заставляет его работать.

person Josh Davis    schedule 16.09.2010

Эрр, ммм? Но спасибо @Jordy за быстрый ответ.

Во-первых, это DOM-XML, что нежелательно, поскольку все остальное в моем сценарии выполняется с помощью SimpleXML.

Во-вторых, почему вы переводите в верхний регистр и ищете неизмененную строку «Найди меня»? «Поиск «НАЙТИ МЕНЯ» на самом деле даст результат.

Но вы указали мне правильное направление:

$nodes = $xml->xpath("//text()[contains(., 'Find me')]");

делает трюк!

person xlttj    schedule 16.09.2010
comment
Тогда проголосуйте за мой ответ как за ответ: D Перевод для верхнего регистра - это когда вы хотите сделать его нечувствительным к регистру. - person ; 16.09.2010

Я искал способ узнать, существует ли узел с точным значением «Найди меня», и это, похоже, сработало.

$node = $xml->xpath("//text()[.='Find Me']");
person Willy    schedule 15.08.2012