RE: [xsl] Xpath related to following-sibling and preceding-sibling

Subject: RE: [xsl] Xpath related to following-sibling and preceding-sibling
From: "Michael Kay" <mhk@xxxxxxxxx>
Date: Wed, 6 Aug 2003 09:26:14 +0100
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@xxxxxxxxxxxxxxxxxxxxxx 
> [mailto:owner-xsl-list@xxxxxxxxxxxxxxxxxxxxxx] On Behalf Of 
> Teresa Rippeon
> Sent: 05 August 2003 19:42
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> 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


Current Thread