Выражение XPath: выбор элементов между тегами A HREF = "expr" - PullRequest
5 голосов
/ 02 июля 2011

Я не нашел явного способа выбрать все узлы, которые существуют между двумя якорями (пара тегов <a></a>) в файле HTML.

Первый якорь имеет следующий формат:

<a href="file://START..."></a>

Второй якорь:

<a href="file://END..."></a>

Я проверил, что оба могут быть выбраны с помощью начальных с (примечаниечто я использую HTML Agility Pack):

HtmlNode n0 = html.DocumentNode.SelectSingleNode("//a[starts-with(@href,'file://START')]"));
HtmlNode n1 = html.DocumentNode.SelectSingleNode("//a[starts-with(@href,'file://END')]"));

Учитывая это и мои любительские навыки XPath, я написал следующее выражение, чтобы получить все теги между двумя якорями:

html.DocumentNode.SelectNodes("//*[not(following-sibling::a[starts-with(@href,'file://START0')]) and not (preceding-sibling::a[starts-with(@href,'file://END0')])]");

Это, кажется, работает, но выбирает все документы HTML!

Мне нужно, например, для следующего фрагмента HTML:

<html>
...

<a href="file://START0"></a>
<p>First nodes</p>
<p>First nodes
    <span>X</span>
</p>
<p>First nodes</p>
<a href="file://END0"></a>

...
</html>

удалить оба якоря, три P (включая, конечно, внутренний SPAN).

Любойспособ сделать это?

Я не знаю, предлагает ли XPath 2.0 более эффективные способы достижения этой цели.

* РЕДАКТИРОВАТЬ (особый случай!) *

Я должен такжеобработать случай, когда:

"Выберите теги между X и X ', где X это <p><a href="file://..."></a></p>"

Так что вместо:

<a href="file://START..."></a>
<!-- xhtml to be extracted -->
<a href="file://END..."></a>

Я должен также обрабатывать:

<p>
  <a href="file://START..."></a>
</p>
<!-- xhtml to be extracted -->

<p>
  <a href="file://END..."></a>
</p>

Большое спасибо, еще раз.

Ответы [ 2 ]

6 голосов
/ 02 июля 2011

Используйте это выражение XPath 1.0 :

//a[starts-with(@href,'file://START')]/following-sibling::node()
     [count(.| //a[starts-with(@href,'file://END')]/preceding-sibling::node())
     =
      count(//a[starts-with(@href,'file://END')]/preceding-sibling::node())
     ]

Или используйте это выражение XPath 2.0 :

    //a[starts-with(@href,'file://START')]/following-sibling::node()
  intersect
    //a[starts-with(@href,'file://END')]/preceding-sibling::node()

The XPath 2.0В выражении используется оператор XPath 2.0 intersect.

В выражении XPath 1.0 используется формула Кейса (после @Mayhael Kay) для пересечения двух наборов узлов:

$ns1[count(.|$ns2) = count($ns2)]

Проверка с помощью XSLT :

Это преобразование XSLT 1.0:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "    //a[starts-with(@href,'file://START')]/following-sibling::node()
         [count(.| //a[starts-with(@href,'file://END')]/preceding-sibling::node())
         =
          count(//a[starts-with(@href,'file://END')]/preceding-sibling::node())
         ]
  "/>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<html>...
    <a href="file://START0"></a>
    <p>First nodes</p>
    <p>First nodes    
        <span>X</span>
    </p>
    <p>First nodes</p>
    <a href="file://END0"></a>...
</html>

дает требуемый, правильный результат :

<p>First nodes</p>
<p>First nodes    
        <span>X</span>
</p>
<p>First nodes</p>

Это преобразование XSLT 2.0 :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  " //a[starts-with(@href,'file://START')]/following-sibling::node()
   intersect
    //a[starts-with(@href,'file://END')]/preceding-sibling::node()
  "/>
 </xsl:template>
</xsl:stylesheet>

при применении ктот же XML-документ (см. выше) снова приводит к желаемому результату .

2 голосов
/ 04 июля 2011

Я добавил специальный случай, который я должен обработать

Для обработки этого специального случая вы можете работать таким же образом, я имею в виду использовать Kayessian(и используйте XPath Visualizer также ;-)).Пересекающиеся наборы узлов изменяются следующим образом:

Пересекающиеся наборы узлов C

    "//p[.//a[starts-with(@href,'file://START')]]
         /following-sibling::node()"

Все последующие элементы p, содержащие a START.

Пересекающийся набор узлов D

"./following-sibling::p[.//a[starts-with(@href,'file://END')]]
    /preceding-sibling::node())"

Все предшествующие братья и сестры p, содержащие a END и следующиеБратья по току p


Теперь вы можете выполнять пересечение как:

C ∩ D

То есть

    "//p[.//a[starts-with(@href,'file://START')]]
            /following-sibling::node()[
            count(.| ./following-sibling::p
                     [.//a[starts-with(@href,'file://END')]]
                       /preceding-sibling::node())
            =
            count(./following-sibling::p
                   [.//a[starts-with(@href,'file://END')]]
                     /preceding-sibling::node())
            ]"

Если вам нужно управлять обеими ситуациями, вы можете продолжить объединение пересекающихся наборов узлов как

(A A B) ∪ (C ∩ D)

Где:

  • Должен использоваться оператор объединения XPath |:
  • наборы узлов A e B уже показаны в @Dimitre'answer
  • наборы узлов C e D - это те, которые показаны в моем ответе.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...