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 ответа:
То, что вы просите, не может быть сделано в одном единственном выражении 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)]
То есть те следующие
Тогда суть проблемы заключается в том, как добраться до узла внешнего контекста изнутри внутреннего предиката - чистый XPath 1.0 не может этого сделать. В XSLT у вас есть функцияp
элементы, которые имеют на один большеpreceding-sibling::p[iframe]
, чем имеет$x
.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