Выбор братьев и сестер между двумя узлами с использованием XPath

Как мне выбрать все таблицы между таблицей, id которой является header_completed, и первой таблицей после того, как header_completed, который имеет выравнивание по центру? Вот html, я выбираю его из:

<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center" class="header_completed"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <--
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <--
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- these 5
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <--
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <--
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920"></table>
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>

Я попытался использовать //table[@id="header_completed"]/following-sibling::node()[following-sibling::table[@align="center"][1]], но это не сработало.

Ответ 1

Я считаю, что это выражение XPath выбирает нужные вам узлы:

//table[@class="header_completed"]/
    following-sibling::table[@align="center"][1]/
        preceding-sibling::table[
            preceding-sibling::table[@class="header_completed"]
        ]

Сначала я перехожу к table с помощью @class="header_completed".

Оттуда я выбираю первую следующую таблицу для сиблинга с @align="center".

Оттуда я выбираю все предыдущие родственные таблицы, у которых есть предыдущий брат, который является таблицей с @class="header_completed".

Ответ 2

Используйте метод Kayessian для node -set-пересечения:

Пересечение двух node -sets $ns1 и $ns2 оценивается следующим выражением XPath:

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

Если у нас есть следующий XML-документ:

<t>
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center" class="header_completed"></table>
    <table border="0" cellpadding="0" cellspacing="1" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="2" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="3" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="4" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="5" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table>
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table>
</t>

, то в соответствии с вопросом мы имеем:

$ns1:

/*/*[@class='header_completed'][1]
                     /following-sibling::*

$ns2:

/*/*[@class='header_completed'][1]
             /following-sibling::*[@align='center'][1]
                   /preceding-sibling::*

Мы просто подставляем $ns1 и $ns2 в формулу Кайсеся и получаем следующее выражение XPath, которое выбирает именно нужные 5 элементов:

/*/*[@class='header_completed'][1]
                         /following-sibling::*
              [count(.|/*/*[@class='header_completed'][1]
                            /following-sibling::*[@align='center'][1]
                               /preceding-sibling::*)
              =
               count(/*/*[@class='header_completed'][1]
                            /following-sibling::*[@align='center'][1]
                                /preceding-sibling::*)
              ]

Чтобы убедиться, что это действительно решение, мы используем это преобразование XSLT:

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

 <xsl:variable name="ns1" select=
      "/*/*[@class='header_completed'][1]
                         /following-sibling::*
      "/>
    <xsl:variable name="ns2" select=
       "/*/*[@class='header_completed'][1]
                 /following-sibling::*[@align='center'][1]
                       /preceding-sibling::*
       "/>

    <xsl:template match="/">
        <xsl:copy-of select=
       "$ns1[count(.| $ns2)=count($ns2)]
       "/>
        <DELIMITER/>
        <xsl:copy-of select=
       "/*/*[@class='header_completed'][1]
                         /following-sibling::*
              [count(.|/*/*[@class='header_completed'][1]
                            /following-sibling::*[@align='center'][1]
                               /preceding-sibling::*)
              =
               count(/*/*[@class='header_completed'][1]
                            /following-sibling::*[@align='center'][1]
                                /preceding-sibling::*)
              ]
       "/>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к XML-документу выше, выдается желаемый правильный результат:

<table border="0" cellpadding="0" cellspacing="1" width="920"/>
<table border="0" cellpadding="0" cellspacing="2" width="920"/>
<table border="0" cellpadding="0" cellspacing="3" width="920"/>
<table border="0" cellpadding="0" cellspacing="4" width="920"/>
<table border="0" cellpadding="0" cellspacing="5" width="920"/>
<DELIMITER/>
<table border="0" cellpadding="0" cellspacing="1" width="920"/>
<table border="0" cellpadding="0" cellspacing="2" width="920"/>
<table border="0" cellpadding="0" cellspacing="3" width="920"/>
<table border="0" cellpadding="0" cellspacing="4" width="920"/>
<table border="0" cellpadding="0" cellspacing="5" width="920"/>

Решение XPath 2.0:

В XPath 2.0 мы можем использовать оператор intersect и операторы >> и/или <<.

Выражение XPath 2.0, которое соответствует ранее используемому выражению XPath 1.0:

     /*/*[ .
        >>
         /*/*[@class='header_completed'][1]
         ]

  intersect

    /*/*[ /*/*[@class='header_completed'][1]
                 /following-sibling::*[@align='center'][1]
             >>
              .
        ]

Вот решение XSLT 2.0, подтверждающее правильность этого выражения 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:variable name="ns1" select=
  "/*/*[ .
        >>
         /*/*[@class='header_completed'][1]
       ]
  "/>

    <xsl:variable name="ns2" select=
       "/*/*[ /*/*[@class='header_completed'][1]
                 /following-sibling::*[@align='center'][1]
             >>
              .
             ]
       "/>

 <xsl:template match="/">
   <xsl:sequence select="$ns1 intersect $ns2"/>
  <DELIMITER/>
   <xsl:sequence select=
   "/*/*[ .
        >>
         /*/*[@class='header_completed'][1]
       ]

  intersect

    /*/*[ /*/*[@class='header_completed'][1]
                 /following-sibling::*[@align='center'][1]
             >>
              .
        ]
   "/>
 </xsl:template>
</xsl:stylesheet>

При применении к документу XML, определенному ранее, мы снова получаем тот же самый желаемый, правильный результат:

<table border="0" cellpadding="0" cellspacing="1" width="920"/>
<table border="0" cellpadding="0" cellspacing="2" width="920"/>
<table border="0" cellpadding="0" cellspacing="3" width="920"/>
<table border="0" cellpadding="0" cellspacing="4" width="920"/>
<table border="0" cellpadding="0" cellspacing="5" width="920"/>
<DELIMITER/>
<table border="0" cellpadding="0" cellspacing="1" width="920"/>
<table border="0" cellpadding="0" cellspacing="2" width="920"/>
<table border="0" cellpadding="0" cellspacing="3" width="920"/>
<table border="0" cellpadding="0" cellspacing="4" width="920"/>
<table border="0" cellpadding="0" cellspacing="5" width="920"/>