XPath - все следующие братья и сестры, кроме первых конкретных элементов

предположим, я запрашиваю документ xhtml, и я хочу запросить всех братьев и сестер после таблицы с id='target'. Кроме того, я не хочу первый <table> брат ни первый <ol> брат этого конкретного элемента. Вот лучшее, что я мог придумать:

//table[@id='target']/following-sibling::*[not(self::table[1]) and not(self::ol[1])]

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

обновление:
См. ответ Ларша для объяснения того, почему мой XPath не работал, и см. ответ Дмитрия для принятого решения.

3 ответов


использовать:

 /table[@id='target']/following-sibling::*[not(self::table) and not(self::ol)] 
| 
 /table[@id='target']/following-sibling::table[position() > 1]
|
 /table[@id='target']/following-sibling::ol[position() > 1]

это выбирает всех следующих братьев и сестер таблицы, которые не являются table и не ol и все следующие table братья и сестры с положением 2 или больше и все следующие ol братья и сестры с положением 2 или выше.

это именно то, что вы хотите: все следующие братья и сестры, за исключением первого table после брата и первого ol следующий братья и сестры.

это чистый XPath 1.0 и не используя никаких функций XSLT.


отвечая на второй вопрос во-первых: то, что выше делает, это выбор всех следующих братьев и сестер, которые не являются ни table, ни ol элементы.

вот почему: self::table[1] выбирает сам контекстный узел (если он передает table имя элемента test) и фильтры, чтобы выбрать только первый узел вдоль оси self::. Существует не более одного узла на оси self::, проходящей тест имени элемента, поэтому [1] избыточна. self::table[1] выбирает контекстный узел всякий раз, когда это элемент таблицы, независимо от своего положения среди своих братьев и сестер. Так что not(self::table[1]) возвращает false, когда контекстный узел является элементом таблицы, независимо от его позиции среди братьев и сестер.

аналогично self::ol[1].

как сделать то, что вы пытаетесь сделать: Ответы @Джон Kugelman-это почти правильно, но не факт, что мы должны игнорировать элементов до и включая table[@id='target']. Я не думаю, что это возможно сделать правильно в чистом XPath 1.0. у вас есть возможность использования XPath 2.0? если вы работаете в браузере, ответ, как правило, нет.

некоторые обходные пути были бы:

  • пропустите первую следующую таблицу sibling и первую следующую ol sibling путем фильтрации на какой-либо другой основе, например, их атрибуты;
  • выберите //table[@id='target'] в качестве nodeset верните его в среду хоста (т. е. вне XPath, например, в JavaScript), выполните цикл через этот nodeset; внутри цикла: выберите following-sibling::* через XPath, повторите через это вне XPath и проверьте каждый результат (вне XPath), чтобы увидеть, является ли это первой таблицей или ol.
  • выберите //table[@id='target'] в качестве nodeset верните его в среду хоста (т. е. вне XPath, например, в JavaScript), выполните цикл через этот nodeset; внутри цикла: выберите generate-id(following-sibling::table[1]) и generate-id(following-sibling::ol[1]) через XPath, получите эти значения в переменные JS t1id и o1id и постройте строку для выражения XPath, используя форму 'following-sibling::*[generate-id() != ' + t1id + ' and generate-id() != ' + o1id + ']'. Выбирать эта строка в XPath, и у вас есть ответ! :- p

обновление: решение и возможно в XSLT 1.0 - смотри @Dimitre это.


есть только один узел, когда вы используете self:: axis, поэтому я считаю self::*[1] всегда будет правдой. Каждый узел будет первым (и единственным) узлом самостоятельно self:: оси. Это означает, что ваше заключенное в скобки выражение эквивалентно [not(self::table) and not(self::ol)], что означает, что все таблицы и списки отфильтровываются.

у меня нет тестовой среды, но с моей головы это может сделать лучше:

/table[@id='target']/following-sibling::*
    [not(self::table and not(preceding-sibling::table)) and
     not(self::ol    and not(preceding-sibling::ol))]

нужно кое-что подправить, но идея фильтра вон!--5-->s, которые не имеют предыдущего брата tableS и ols, которые не имеют предыдущего брата ols.