xslt dynamically start and close tags

2019-03-05 09:10发布

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

标签: xslt
2条回答
Melony?
2楼-- · 2019-03-05 09:40

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>
查看更多
Evening l夕情丶
3楼-- · 2019-03-05 09:45

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.

查看更多
登录 后发表回答