XPath (1.0) сопоставляет последовательные элементы до определенного дочернего элемента или конца


Это для XPath 1.0.

Вот пример отметки, с которой я сопоставляю. Фактическое число элементов не известно заранее и, таким образом, изменяется, но следует такой схеме:

<div class="entry">
    <p><iframe /></p>
    <p>Text 1</p>
    <p>Text 2</p>
    <p>Test 3</p>
    <p><iframe /></p>
    <p>
        <a>Test 4</a>
        <br />
        <a>Test 5</a>
    </p>
</div>

Я пытаюсь соответствовать каждому <p>, что делает Нет содержать <iframe>, вплоть до следующего <p>, которое делает содержать элемент <iframe> или до конца заключающего элемента <div>.

Чтобы делать вещи немного сложнее, по определенным причинам мне нужно использовать каждый <iframe> в качестве базы, a la //div[@class='entry']//iframe, так что каждый набор узлов основан на

(//div[@class='entry']//iframe)[1]
(//div[@class='entry']//iframe)[2]
...

И таким образом, в данном случае совпадение

<p>Text 1</p>
<p>Text 2</p>
<p>Test 3</p>

И

<p>
    <a>Test 4</a>
    <br />
    <a>Test 5</a>
</p>

Соответственно.

Я попробовал некоторые из следующих тестов безрезультатно:

(//div[@class='entry']//iframe)/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]

(или для проверки):

(//div[@class='entry']//iframe)[1]/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]
(//div[@class='entry']//iframe)[2]/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]

И некоторые его вариации, но то, что происходит для первого набора, это то, что он получает все <iframe>-меньше <p> элементов вплоть до конца вместо того, чтобы останавливаться на следующем <p>, содержащем <iframe>.

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

Спасибо. Любая помощь всегда ценится.

Edit: можно предположить, что в документе есть только одно вхождение <div class="entry">.

2 2

2 ответа:

То, что вы просите, не может быть сделано в одном единственном выражении XPath 1.0 без помощи. Проблема в том, что вопрос, который вы хотите задать, является

Начиная с элемента X (p-содержащего-iframe), найдите другие p элементы, для которых ближайший предшествующий P-с-iframe элемент является исходным узлом X

Если бы у нас была переменная $x, содержащая ссылку на контекстный узел верхнего уровня (p[iframe], с которого мы начинаем), то вы могли бы что-то сказать например, следующее (В XPath 2.0)

following-sibling::p[not(iframe)][preceding-sibling::p[iframe][1] is $x]

XPath 1.0 не имеет оператора is Для сравнения идентичности узлов, но есть другие прокси, которые вы можете использовать для этого, например

following-sibling::p[not(iframe)][count(preceding-sibling::p[iframe])
                               = (count($x/preceding-sibling::p[iframe]) + 1)]

То есть те следующие p элементы, которые имеют на один больше preceding-sibling::p[iframe], чем имеет $x.

Тогда суть проблемы заключается в том, как добраться до узла внешнего контекста изнутри внутреннего предиката - чистый XPath 1.0 не может этого сделать. В XSLT у вас есть функция current(), но в остальном у вас есть два основные варианты:
  • Если ваша библиотека XPath позволяет вам предоставлять привязки переменных к вашим выражениям, то введите переменную $x, содержащую узел контекста, и используйте выражение, которое я дал выше.
  • Если вы не можете ввести переменные, то используйте два отдельных запроса XPath в последовательности.

Сначала выполните выражение

count(preceding-sibling::p[iframe]) + 1

С соответствующим p[iframe] в качестве контекстного узла и принять результат в виде числа. Или альтернативно, если вы уже выполняете итерацию над этими p[iframe] элементами в вашем хост-языке, то просто возьмите номер итерации оттуда непосредственно, вам не нужно подсчитывать его с помощью XPath. В любом случае, вы можете построить второе выражение динамически:

following-sibling::p[not(iframe)][count(preceding-sibling::p[iframe]) = N]

(где N - результат первого выражения/счетчика итераций) и вычислить его с тем же контекстным узлом, принимая конечный результат как набор узлов.

Я не уверен, что понял полностью, но иногда это помогает прокомментировать попытку решения, а не пытаться объяснить.

Попробуйте следующее выражение XPath:

//div[@class='entry']//iframe//p[not(descendant::iframe)]
И дайте мне знать, если это даст правильный результат.

Если нет, то

  • объясните, чем отличается результат от того, что вам нужно
  • пожалуйста, покажите более полный пример HTML: разумный документ с несколькими div элементами и более одного, где div[@class = 'entry'] - и в противном случае охватывая всю сложность, которую вы описываете.
  • Объясните, почему вы добавили [1] и [2] к вашим выражениям
  • дайте более подробную информацию о платформе, с которой вы используете XPath, возможно, post code