sanjay
sanjay

Reputation: 1020

XSLT - Using for-each-group for following siblings

I've xml like follows,

<doc>
    <h1>header 1</h1>
    <p>para</p>
    <p>para</p>

    <h1>header 2</h1>
    <p>para</p>
    <h2>header 2 sub h1</h2>
    <h2>header 2 sub h2</h2>

    <h1>header 3</h1>
    <p>para</p>
    <p>para</p>

    <h1>header 4</h1>
    <h2>header 4 sub h1</h2>
    <p>para</p> 

</doc>

what I need is get all <h1> and <h2> nodes respectively and create an unordered list with links,

so the desired output is,

<doc>
   <ul>
      <li><h1><a href="page-header1-1">header 1</a></h1></li>

      <li><h1><a href="page-header1-2">header 2</a></h1></li>
         <ul> 
            <li><h2><a href="page-header2-1">header 2 sub h1</a></h2></li>
            <li><h2><a href="page-header2-2">header 2 sub h2</a></h2></li>
         </ul>

      <li><h1><a href="page-header1-3">header 3</a></h1></li>

      <li><h1><a href="page-header1-4">header 4 sub h1</a></h1></li>
         <ul>
            <li><h2><a href="page-header2-3">zz</a></h2></li>
         </ul>
   </ul>
</doc>

I've written following xsl to achieve this task,

<xsl:template match="doc">
        <div>
            <ul>
                <xsl:for-each-group select="*" group-starting-with="h1">
                    <li><a href="page-header1-{count(preceding::h1)+1}"> <xsl:copy-of select="."/></a> </li>
                    <ul>
                        <li><a href="page-header2-{count(preceding::h2)+1}"><xsl:apply-templates select="current-group()[self::h2]"/></a></li>
                    </ul>
                </xsl:for-each-group>
            </ul>
        </div>
    </xsl:template>

my output is looks like this,

 <doc>
    <ul>
        <li><a href="page-header1-1"><h1>header 1</h1></a></li>
            <ul><li><a href="page-header2-1"/></li></ul>

        <li><a href="page-header1-2"><h1>header 2</h1></a></li>
            <ul>
                <li>
                    <a href="page-header2-1">
                        <h2>header 2 sub h1</h2>
                        <h2>header 2 sub h2</h2>
                    </a>
                </li>
            </ul>

        <li><a href="page-header1-3"><h1>header 3</h1></a></li>
            <ul><li><a href="page-header2-3"/></li></ul>

        <li><a href="page-header1-4"><h1>header 4</h1></a></li>
            <ul>
                <li>
                    <a href="page-header2-3">
                        <h2>header 4 sub h1</h2>
                    </a>
                </li>
            </ul>
    </ul>
</doc>

I've identified 2 errors there

1) it adds empty <a href="page-header2-x"></a> values where there are no following siblings for <h1>s. (see the results of 1st and 3rd <h1> s)

2) <a> are not separately added to <h2> nodes. but it adds <a> to group of <h2>s which are consecutively placed. (see under 2nd <h1> in the result) But I need to add <a> to separately for every <h2>.

I've used <xsl:apply-templates select="current-group()[self::h2]"/> to get the <h2> s and it seems it returns all <h2> as a group but not returns separately.

Can anyone suggest me a method to sort out this problem..

Upvotes: 0

Views: 202

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167516

Change <ul> <li><a href="page-header2-{count(preceding::h2)+1}"><xsl:apply-templates select="current-group()[self::h2]"/></a></li> </ul> to

<xsl:variable name="h2" select="current-group()[self::h2]"/> <xsl:if test="$h2"> <ul> <xsl:apply-templates select="$h2"/> </ul> </xsl:if>

and add a template <xsl:template match="h2"> <li><a href="page-header2-{position()"><xsl:copy-of select="."/></a></li> </xsl:template>

Upvotes: 1

Related Questions