This is a classical "positional grouping" problem. In XSLT 2.0 you can
do it using
<xsl:for-each-group group-adjacent="name()">
but in 1.0 it's more complex. There are two approaches. One is to treat
it as a value-based grouping problem, using Muenchian grouping with a
grouping key that is the generate-id() of the first Para in a group of
consecutive Para elements. The other way is to use apply-templates
recursively in the following-sibling direction.
<xsl:template match="BlockContent">
<xsl:apply-templates select="*[1]" mode="across"/>
</xsl:template>
<xsl:template match="*" mode="across">
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::*[1]" mode="across"/>
</xsl:template>
<xsl:template match="Para" mode="across">
<description>
<xsl:apply-templates select="."/>
<xsl:if test="following-sibling::*[1][self::Para]">
<xsl:apply-templates select="following-sibling::*[1]"
mode="para-across"/>
</xsl:if>
</description>
<xsl:apply-templates select="following-sibling::*[not(self::Para)][1]"
mode="across"/>
</xsl:template>
<xsl:template match="Para" mode="para-across">
<xsl:apply-templates select="."/>
<xsl:if test="following-sibling::*[1][self::Para]">
<xsl:apply-templates select="following-sibling::*[1]"
mode="para-across"/>
</xsl:if>
</xsl:template>
Not tested, and can be simplified.
Michael Kay
-----Original Message-----
From: owner-xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
[mailto:owner-xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com] On Behalf Of
Teresa Rippeon
Sent: 05 August 2003 19:42
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] Xpath related to following-sibling and
preceding-sibling
I have the following example XML data with the desired output:
<Block id="block1">
<BlockContent>
<Para>This is para 1 in group 1</Para>
<Para>This is para 2 in group 1</Para>
<list><li>List item 1</li><li>List item 2</li></list>
<Para>This is para 1 in group 2</Para>
<Para>This is para 2 in group 2</Para>
<Para>This is para 3 in group 2</Para>
<Para>This is para 4 in group 2</Para>
</BlockContent>
</Block>
With the following desired output:
<description id="Block1_P1">
<p>This is para 1 in group 1</p>
<p>This is para 2 in group 1</p>
</description>
<list> .... </list>
<description id="Block1_P4">
<p>This is para 1 in group 2</p>
<p>This is para 2 in group 2</p>
<p>This is para 3 in group 2</p>
<p>This is para 4 in group 2</p>
</description>
I have the following portion of the XSLT, which generates...
<description id="Block1_P1">
<p>This is para 1 in group 1</p>
<p>This is para 2 in group 1</p>
<p>This is para 1 in group 2</p>
<p>This is para 2 in group 2</p>
<p>This is para 3 in group 2</p>
<p>This is para 4 in group 2</p></description>
<list> .... </list>
I know that I should have a check for a following-sibling
that is not a Para, but as yet I have not been able to come
up with the correct Xpath expression to complete this. Any
suggestions? Thanks!
<xsl:for-each
select="//BlockContent/Para|//BlockContent/List|//BlockContent/Table">
<!-- PARAs -->
<xsl:if test="self::Para">
<xsl:choose>
<!-- When the preceding sibling was a para, then
the description element has already been established
and the para was appended into the
description element in a previous for loop; don't want to write
it out again -->
<xsl:when test="preceding-sibling::Para">
</xsl:when>
<!-- Otherwise, establish the description
element and put all consecutive paras within it as a chunk -->
<xsl:otherwise>
<xsl:call-template
name="FormatXMLOutput"/>
<xsl:element name="description">
<xsl:attribute
name="identifier">
<xsl:value-of
select="ancestor::Block[1]/@ID"/>
<xsl:text>_P</xsl:text>
<xsl:number count="Para"
from="BlockContent" level="single"/>
</xsl:attribute>
<xsl:call-template
name="FormatXMLOutput"/>
<xsl:element name="p">
<xsl:apply-templates/>
</xsl:element>
<!-- Now loop through all of the
siblings of the Para so that all consecutive paras are
"chunked" together in
the description element.
Note that the siblings are defined as on the same level so if
there was a list
between this para and the
previous sibling para, then you don't want this in the same
description
element "chunk" -->
<xsl:for-each
select="following-sibling::Para">
<xsl:element name="p">
<xsl:apply-templates/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
Etc. specifically dealing with lists, tables, etc.
Thanks!
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list