Из вашего комментария в ответ на ответ Рубенса Фариаса (и действительно, это то, что вы должны отредактировать, чтобы включить свой вопрос), кажется, что вы хотите универсальный способ преобразовать любую группу смежных BulletText
элементов в список. Это приводит нас к двум вопросам: как мы находим такие группы, и, найдя их, как мы превращаем их в список?
Чтобы найти группу, нам нужно найти все элементы BulletText
, чей непосредственно предшествующий элемент не является элементом BulletText
. Каждый из них создает группу, и это те элементы, которые мы собираемся преобразовать в списки. Поэтому первое, что мы хотим сделать, это создать выражение XPath, которое найдет их:
BulletText[not(preceding-sibling::*[1][name()='BulletText'])]
Если вы посмотрите на предикаты в этом выражении XPath, он делает то, что я сказал, что нам нужно сделать: он соответствует элементу BulletText
, если это не тот случай, когда его первый предшествующий брат (preceding-sibling::*[1]
) имеет имя BulletText
. Обратите внимание, что если элемент не имеет предшествующего брата, это выражение все равно будет ему соответствовать.
Итак, теперь мы можем создать шаблон, который соответствует этим элементам начала группы. Что мы помещаем в этот шаблон? Мы собираемся преобразовать эти элементы в LIST
элементы, поэтому шаблон будет выглядеть примерно так:
<LIST>
...
</LIST>
Достаточно просто. Но как нам найти элементы, которые будут заполнять этот список? Есть два случая, с которыми нам приходится иметь дело.
Первый прост: если все следующие братья и сестры являются BulletText
элементами, мы хотим заполнить список этим элементом и всеми его следующими братьями и сестрами.
Второй сложнее. Если есть следующий родственный элемент, который не является элементом BulletText
, мы хотим, чтобы наш список был всеми дочерними элементами родительского элемента текущего элемента, начиная с текущего элемента и заканчивая перед элементом stop. Вот случай, когда нам нужно использовать функцию count()
для вычисления начального и конечного индексов и функцию position()
для определения позиции каждого элемента.
Готовый шаблон выглядит так:
<xsl:template match="BulletText[not(preceding-sibling::*[1][name()='BulletText'])]">
<!-- find the element that we want to stop at -->
<xsl:variable name="stop" select="./following-sibling::*[name() != 'BulletText'][1]"/>
<LIST>
<xsl:choose>
<!-- first, the simple case: there's no element we have to stop at -->
<xsl:when test="not($stop)">
<xsl:apply-templates select="." mode="item"/>
<xsl:apply-templates select="./following-sibling::BulletText" mode="item"/>
</xsl:when>
<!-- transform all elements between the start and stop index into items -->
<xsl:otherwise>
<xsl:variable name="start_index" select="count(preceding-sibling::*) + 1"/>
<xsl:variable name="stop_index" select="count($stop/preceding-sibling::*)"/>
<xsl:apply-templates select="../*[position() >= $start_index
and position() <= $stop_index]"
mode="item"/>
</xsl:otherwise>
</xsl:choose>
</LIST>
</xsl:template>
Вам нужны два других шаблона. Каждый преобразует BulletText
элементов в элементы - мы используем mode
здесь, чтобы мы могли применить его к BulletText
элементам, не вызывая шаблон, который мы используем в настоящее время:
<xsl:template match="BulletText" mode="item">
<ITEM>
<xsl:value-of select="."/>
</ITEM>
</xsl:template>
Тогда вам также понадобится шаблон, который хранит BulletText
элементов, которые не соответствуют нашему первому шаблону при генерации какого-либо вывода (потому что, если мы используем преобразование идентичности, они просто будут скопированы если мы этого не сделаем):
<xsl:template match='BulletText'/>
Из-за волшебства правил приоритета шаблонов XSLT любой элемент BulletText
, которому соответствуют оба шаблона, будет преобразован первым, а этот поймает остальные.
Просто добавьте эти три шаблона в преобразование идентичности, и все готово.