xsl-list
[Top] [All Lists]

RE: Xpath related to following-sibling and preceding-sibling

2003-08-06 01:26:14
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



<Prev in Thread] Current Thread [Next in Thread>