Хобрук: Ваш путь к мастерству в программировании

XPath Выбор узлов до условия

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

<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>

Я ищу выражение XPath (1.0), которое, начиная с первого 'tr' в любом цветовом 'блоке' (обратите внимание, что нет разметки, указывающей на эти блоки, только изменения в цвете ), выбирает следующие последующие 'tr' только в этом блоке.

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

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

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

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

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

29.03.2012

  • Я немного запутался... Можете ли вы тоже опубликовать желаемый результат? 29.03.2012
  • Например, если бы моей отправной точкой была 3-я 'tr' (красная), я бы выбрал только 4-ю и 5-ю 'tr'. 29.03.2012

Ответы:


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::*
         )
         ]

Пояснение:

Используя известную формулу Кайеса для пересечения множества узлов:

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

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

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

Проверка на основе 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"/>
29.03.2012
  • Спасибо за ответ. Тем не менее, я ищу общий XPath для выбора любого блока, используя его первый ‹tr› в качестве отправной точки. Я понимаю концепцию пересечения множества и пытаюсь сам переписать выражение, но не могу заставить его работать. Если у вас есть время, я был бы признателен за общее решение :) 29.03.2012
  • @ user1300244: Этот ответ полностью решает проблему, которая в настоящее время описана в вопросе. Если у вас есть другие требования, отредактируйте вопрос и точно укажите узлы, которые вы хотите, чтобы XPath выбрал. 29.03.2012
  • Извините, я не согласен. В вопросе говорится, что я ищу выражение XPath (1.0), которое, начиная с первого «tr» в цветном «блоке» любого, выбирает следующие последующие «tr» только внутри этого блока. 29.03.2012
  • @ user1300244: Это вообще не совсем четко определено. Что означает любой блок? Это необходимо указать точно, если вы хотите, чтобы выражение XPath выбирало нужные узлы. Например, выражение в моем ответе показывает, как выбрать нужные узлы для первого синего блока. Вам нужно указать какой блок — без использования ссылок на переменные вы не можете указать какой-либо блок. 29.03.2012
  • Ах, это то, что я надеялся, что смогу сделать. Спасибо за ваше время, я кое-что узнал, даже если это не был таким полным ответом на мой вопрос, как я надеялся. 29.03.2012
  • УДИВИТЕЛЬНО. Снимаю шляпу. Спас меня от МНОГО неприятностей 26.07.2018
  • @RajVJain, добро пожаловать. Да, XPath и XSLT потрясающие 26.07.2018

  • 2

    Если у вас есть переменная $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.

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

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

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

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..