Выберите весь элемент между текущим элементом и следующим элементом текущего элемента

Используя XSLT 1.0 (предпочтительно), как я могу выбрать все элементы, которые встречаются между текущим элементом и следующим появлением текущего элемента?

Скажем, у меня есть этот XML (отредактированный):

<root>
    <heading_1>Section 1</heading_1>
    <para>...</para>
    <list_1>...</list_1>
    <heading_2>Section 1.1</heading_2>
    <para>...</para>
    <heading_3>Section 1.1.1</heading_3>
    <para>...</para>
    <list_1>...</list_1>
    <heading_2>Section 1.2</heading_2>
    <para>...</para>
    <footnote>...</footnote>
    <heading_1>Section 2</heading_1>
    <para>...</para>
    <list_1>...</list_1>
    <heading_2>Section 2.1</heading_2>
    <para>...</para>
    <list_1>...</list_1>
    <list_2>...</list_2>
    <heading_3>Seciton 2.1.1</heading_3>
    <para>...</para>
    <heading_2>Section 2.2</heading_2>
    <para>...</para>
    <footnote>...</footnote>
</root>

При обработке heading_1 я хочу выбрать все heading_2 между обрабатываемым заголовком и следующим heading_1. То же самое для выбора heading_3 при обработке heading_2 и т. д. вы получаете изображение.


person jorbas    schedule 22.04.2013    source источник
comment
Jrod: Я открыл новый вопрос на основе вашего измененного описания. Потому что я думаю, что это интересная проблема. Также я положил первый возможный ответ там.   -  person hr_117    schedule 24.04.2013


Ответы (2)


Вы можете использовать это:

following-sibling::heading_2[generate-id(preceding-sibling::heading_1[1]) = 
                             generate-id(current())]

Рабочий пример:

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

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="heading_1" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="heading_1">
    <xsl:copy>
      <xsl:apply-templates
        select="following-sibling::heading_2[generate-id(
                                                preceding-sibling::heading_1[1]) = 
                                             generate-id(current())]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="heading_2">
    <xsl:copy>
      <xsl:apply-templates
        select="following-sibling::heading_3[generate-id(
                                                preceding-sibling::heading_2[1]) = 
                                             generate-id(current())]" />
    </xsl:copy>
  </xsl:template>    
</xsl:stylesheet>

Результат при запуске на вашем образце ввода:

<root>
  <heading_1>
    <heading_2>
      <heading_3>...</heading_3>
    </heading_2>
    <heading_2 />
  </heading_1>
  <heading_1>
    <heading_2>
      <heading_3>...</heading_3>
    </heading_2>
    <heading_2 />
  </heading_1>
</root>
person JLRishe    schedule 22.04.2013
comment
Не могли бы вы объяснить выбор? Я пытаюсь изменить его так, чтобы я мог сохранить все остальные элементы, чем в вашем примере, которые были вырезаны. Например, при обработке heading_1 я пытаюсь использовать following-sibling::*[generate-id(preceding-sibling::heading_2[1]) = generate-id(current())], но это, похоже, ничего не выбирает. - person jorbas; 22.04.2013
comment
+1, хороший ответ. @BigJord: попробуйте изменить heading_2 на heading_1 во фрагменте кода, который вы только что дали. Я оставлю JLRishe объяснение выражения XPath. - person LarsH; 22.04.2013
comment
@BigJord generate-id() создает уникальный идентификатор для любого узла и может использоваться (как в моем примере), чтобы определить, когда два выбранных узла на самом деле являются одним и тем же узлом. Учитывая это, выражение, которое я дал, может быть прочитано на человеческом языке как что-то вроде выбора всех следующих heading_2, у которых первый предшествующий heading_1 является тем же узлом, что и текущий узел. Как говорит LarsH, ваше выражение должно работать, если вы измените heading_2 на heading_1. - person JLRishe; 22.04.2013
comment
По какой-то причине following-sibling::*[generate-id(preceding-sibling::heading_1[1]) = generate-id(current())] не работает в моем XSLT. Вместо того, чтобы выбирать всех следующих братьев и сестер до следующего заголовка_1, он выбирает остальную часть документа, начиная с этого заголовка и далее (а затем делает это снова для следующего заголовка и т. д.). Это почему? - person jorbas; 23.04.2013
comment
Точно сказать не могу. Не могли бы вы опубликовать текущее состояние вашего XSLT в своем вопросе выше? - person JLRishe; 23.04.2013
comment
Я добавил еще несколько деталей к исходному вопросу. - person jorbas; 23.04.2013

Попробуйте приведенный ниже XPath, чтобы выбрать заголовок 2 при обработке заголовка 1.

(/root/heading_1/following-sibling::heading_1/preceding-sibling::heading_2) | (/root/heading_1[preceding-sibling::heading_1]/following-sibling::heading_2)
person siva2012    schedule 22.04.2013
comment
Это будет просто каждый раз выбирать все heading_2 в документе. - person JLRishe; 22.04.2013
comment
@JLRishe: на самом деле будут выбраны только те элементы heading_2, которые являются дочерними элементами элемента /root; и только в том случае, если есть как минимум два элемента heading_1, которые также являются дочерними элементами элемента /root. С данным образцом входных данных эти предостережения не имеют никакого значения, но не ясно, всегда ли будет как минимум два heading_1. - person LarsH; 22.04.2013
comment
@LarsH Хорошие моменты. Я пропустил часть того, что вы сказали для краткости (предполагая, что документ всегда будет иметь общую структуру, указанную в OP), и забыл подумать о том, что произойдет, если будет только один heading_1. Однако я хочу сказать, что этот ответ вообще не решает данный вопрос. - person JLRishe; 22.04.2013