2012-03-29 2 views
2

У меня есть документ HTML/XML, похожий на следующий. При переключении на другой цвет в произвольно повторяющемся шаблоне может быть один или несколько «tr» одного цвета. Это пример:XPath Выбор узлов до условия

<tr class='red'></tr> 
<tr class='blue'></tr> 
<tr class='red'></tr> 
<tr class='red'></tr> 
<tr class='red'></tr> 
<tr class='blue'></tr> 
<tr class='blue'></tr> 
<tr class='red'></tr> 
<tr class='red'></tr> 
<tr class='blue'></tr> 

То, что я ищу это (1,0) выражение XPath, которое, начиная с первого «тр» в любом цвете «блок» (обратите внимание, что нет нет разметки с указанием их блоки, только изменения в цвете), выбирает только следующие последующие «tr» внутри этого блока.

Я попытался следующее выражение

./following-sibling::tr[@class=preceding-sibling::tr[1]/@class] 

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

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

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

+0

Я немного смущен ... Можете ли вы также опубликовать желаемый результат? –

+0

Так, например, если моя начальная точка была 3-й 'tr' (красный), я бы выбрал только 4-й и 5-й 'tr. – user1300244

ответ

1

этот XPath 1.0 выражение выбирает первый "блок" голубых tr элементов:

 (/*/tr[@class='blue'][1] | /*/tr[@class='blue'][1]/following-sibling::tr) 
     [count(. | /*/tr[@class='blue'][1] 
          /following-sibling::tr 
            [not(@class='blue')][1] 
             /preceding-sibling::* 
       ) 
     = 
     count(/*/tr[@class='blue'][1] 
          /following-sibling::tr 
            [not(@class='blue')][1] 
             /preceding-sibling::* 
     ) 
     ] 

Объяснение:

Использование хорошо известный формулу Kayessian для пересечения набор узлов:

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

Это выражение XPath точно выбирает узлы, которые принадлежат как узлу $ns1, так и набор узлов $ns2.

В данном конкретном случае мы просто заменить $ns1 и $ns2 с их соответствующими конкретными выражениями XPath - это один первый синий tr и все его следующих братьев и сестер, а другой является первым не-синий tr после первого синего tr и все его предыдущие братья и сестры. Пересечение этих двух наборов узлов - это точно требуемый первый блок синего tr.

XSLT - на основе проверки:

<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="node()|@*"> 
    <xsl:copy-of select= 
    "(/*/tr[@class='blue'][1] | /*/tr[@class='blue'][1]/following-sibling::tr) 
      [count(. | /*/tr[@class='blue'][1] 
           /following-sibling::tr 
             [not(@class='blue')][1] 
              /preceding-sibling::* 
        ) 
      = 
      count(/*/tr[@class='blue'][1] 
           /following-sibling::tr 
             [not(@class='blue')][1] 
              /preceding-sibling::* 
       ) 
      ] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

когда это преобразование применяется на следующий документ XML:

<t> 
    <tr class='red'></tr> 
    <tr class='red'></tr> 
    <tr class='red'></tr> 
    <tr class='red'></tr> 
    <tr class='blue'></tr> 
    <tr class='blue'></tr> 
    <tr class='red'></tr> 
    <tr class='red'></tr> 
    <tr class='blue'></tr> 
</t> 

вычисляется выражение XPath, и выбранные узлы будут скопированы к выходу:

<tr class="blue"/> 
<tr class="blue"/> 
+0

Спасибо за ответ. Тем не менее, я ищу общий XPath для выбора любого блока, используя первый в качестве начальной точки. Я понимаю концепцию пересечения множества, и я пытаюсь переписать выражение сам, но я не могу заставить его работать. Если у вас есть время, я был бы признателен за общее решение :) – user1300244

+0

@ user1300244: Этот ответ полностью решает проблему, которая в настоящее время описана в вопросе. Если у вас есть другие требования, отредактируйте вопрос и укажите точно, какие узлы вы хотите выбрать XPath. –

+0

Извините, я не согласен. В вопросе говорится: «Я ищу выражение XPath (1.0), которое, начиная с первого« tr »в ** любом цветовом« блоке », выбирает следующие последующие« tr »только в этом блоке». – user1300244

0

Если у вас есть переменная $ V связанный с начальным узлом, то я думаю, что это может быть сделано (с ужасающей неэффективностью), как это:

$v/following-sibling::tr[@class = $v/@class and count(preceding-sibling::tr[not(@class=$v/@class)] = count($v/preceding-sibling::tr[not(@class=$v/@class)])] 

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

Вы не сказали, каковы ваши ограничения, но XPath 1.0 не кажется хорошим выбором технологии для этой конкретной проблемы.

Даже в XPath 2.0 это не особенно приятно. Вам действительно нужна рекурсия, и это подразумевает использование XQuery или XSLT, а не чистого XPath.

+0

К сожалению, я не могу связать переменную. Ограничения заключаются в том, что я могу использовать XPath 1.0, но неэффективность не является проблемой из-за небольших наборов данных. Спасибо за время ответить хотя :) – user1300244

Смежные вопросы