tags:

views:

19

answers:

2

I want to do something similar to the following:

<xsl:for-each select="Item">
  <xsl:if test="postion()=1 or position()=7>
    <ul>
  </xsl:if>
  <li>An Item</li>
  <xsl:if test="position()=2">
    </ul>
  </xsl>
</xsl:for-each>
</ul>

This will not work however because xslt views the unclosed <ul> within the if statement as invalid.

Sample input:

<Item>1</Item>
<Item>2</Item>

<Item>3</Item>

<Item>4</Item>

<Item>5</Item>

<Item>6</Item>

<Item>7</Item>

<Item>8</Item>

<Item>9</Item>

<Item>10</Item>

Expected output:

<ul>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
</ul>
<ul>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
<li>An Item<li>
</ul>

Thanks -Ben

+2  A: 

You should rewrite it to avoid the need for such tag soup. First group the items, such that each group is transformed to its own UL in the output, and then iterate over those groups (and for each group, iterate over the items within in).

For the particular code that you've posted, with hardcoded values, it's trivially refactored by moving the output for Item into its own template (a good idea, anyway), and then reusing that:

<xsl:template match="Item">
    <li>An Item</li>
</xsl:template>

...

<ul>
    <xsl:apply-templates select="Item[1]"/>
</ul>
<ul>
    <xsl:apply-templates select="Item[position() &ge; 2 and position() &le; 7]"/>
</ul>
<xsl:apply-templates select="Item[position() &gt; 7]"/>

In practice, though, you probably has some more complicated way of determining the boundaries of your groups, but without knowing the exact requirement, it's hard to be more specific when answering this. You'll likely want to look at the Muenchian method of grouping if you use some key. If your groups are all fixed-size, with a few hardcoded exceptions (e.g. first item in its own group, and then every next 10 items form a new group), you could iterate over position instead.

Pavel Minaev
Thanks, this is almost exactly the approach I ended up using (with a little more logic to get done what I needed to do...) Much thanks
Ben
+1  A: 

If want you want is simply to group items into groups of six (or fewer, at the end) then you can use a recursive call to a template that spits out the first six Items in a list and then calls itself for any left over.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:transform  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                version="1.0">
<xsl:output method="xml" indent="no" encoding="ISO-8859-1"/>

<xsl:template match="Item">
<li>Item</li>
</xsl:template>

<xsl:template name="group-of-six">
    <xsl:param name="items"/>

    <ul>
    <xsl:for-each select="$items[position() &lt; 7]">
    <xsl:apply-templates select="."/>
    </xsl:for-each>
    </ul>

    <xsl:if test="count($items) &gt; 6">
        <xsl:call-template name="group-of-six">
            <xsl:with-param name="items" select="$items[position() &gt; 6]"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template match="/">
    <Lists>
    <xsl:call-template name="group-of-six">
        <xsl:with-param name="items" select="//Item"/>
    </xsl:call-template>
    </Lists>
</xsl:template>

</xsl:transform>
Vincent Marchetti