Dannie
Dannie

Reputation: 65

Nested list with number labels

I have a nested list, and therefore I want the label to be nested too.

This is my code:

<!-- For first list -->
<xsl:template name="list1-label">
    <xsl:number format="1."/>
</xsl:template>

<!-- The nestled list -->
<xsl:template name="list2-label">
    <xsl:number format="1.1."/>
</xsl:template>

The first number in the last template (1.1.) I want to be relative to the parent item, in this case; "3".

Here is the xml structure:

<list1>
  <item>Test</item>
  <item>Test</item>
  <list2>
    <item>Test</item>
    <item>Test</item>
  </list2>
  <item>Test</item>
</list1>

Here is the output:

1. Test
2. Test
    2.1. Test
    2.2. Test
3. Test

Upvotes: 2

Views: 827

Answers (3)

Emiliano Poggi
Emiliano Poggi

Reputation: 24826

The wanted output can be obtained (for example) applying the advanced attributes @from and @count of xsl:number. Here a working example from which you should get started:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="list1/item">  
        <xsl:number level="any" from="list1" count="list1/item"/>
        <xsl:text>. </xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>

    <xsl:template match="list1/list2/item">
        <xsl:text>&#09;</xsl:text>
        <xsl:number level="any" from="list1" count="list1/item"/>
        <xsl:text>.</xsl:text>
        <xsl:number level="any" from="list2" count="list2/item"/>
        <xsl:text>. </xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

If you want to simplify the match patterns:

<xsl:template match="list1">
    <xsl:apply-templates />
</xsl:template>

<xsl:template match="list1/item">  
    <xsl:number level="any" from="list1" count="list1/item"/>
    <xsl:text>. </xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>

<xsl:template match="list2/item">
    <xsl:text>&#09;</xsl:text>
    <xsl:number level="any" from="list1" count="list1/item"/>
    <xsl:text>.</xsl:text>
    <xsl:number level="any" from="list2" count="list2/item"/>
    <xsl:text>. </xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>

This latest example will correctly drive you to further nesting levels as with a list3.

Upvotes: 1

Emiliano Poggi
Emiliano Poggi

Reputation: 24826

This new transform is generalized to handle a variable number of lists.

Input sample:

<list1>
  <item>Test</item>
  <item>Test</item>
  <list2>
        <item>Test</item>
        <list3>
            <item>Test</item>
            <item>Test</item>
        </list3>
        <item>Test</item>
        <list3>
            <item>Test</item>
        </list3>
        <item>Test</item>
  </list2>
  <item>Test</item>
</list1>

transform:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="*[starts-with(name(),'list')]">
        <xsl:apply-templates>
            <xsl:with-param name="tab" select="true()"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="item">
        <xsl:param name="lvl" select="count(ancestor::*)"/>
        <xsl:param name="tab" select="false()"/>

        <!-- insert entry point tab -->
        <xsl:apply-templates select="text()[$tab and $lvl > 1]" mode="tab">
            <xsl:with-param name="lvl" select="$lvl - 1"/>
        </xsl:apply-templates>

        <!-- recurse levels -->
        <xsl:variable name="name" select="name(ancestor::*[$lvl])"/>
        <xsl:number level="any" from="*[name()=$name]" 
            count="*[name()=$name]/item"/>
        <xsl:text>.</xsl:text>
        <xsl:apply-templates select="self::item[$lvl > 1]">
            <xsl:with-param name="lvl" select="$lvl - 1"/>
        </xsl:apply-templates>

        <!-- print value -->
        <xsl:if test="$lvl = 1">
            <xsl:text> </xsl:text>
            <xsl:value-of select="."/>
            <xsl:text>&#10;</xsl:text>
        </xsl:if>
    </xsl:template>

    <xsl:template match="text()" mode="tab">
        <xsl:param name="lvl"/>
        <xsl:text>&#09;</xsl:text>
        <xsl:apply-templates select="self::text()[$lvl>1]" mode="tab">
            <xsl:with-param name="lvl" select="$lvl - 1"/>
        </xsl:apply-templates>
    </xsl:template>

</xsl:stylesheet>

output:

1. Test
2. Test
    2.1. Test
        2.1.1. Test
        2.1.2. Test
    2.2. Test
        2.2.1. Test
    2.3. Test
3. Test

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163549

For the nested list you should probably be using level="multiple" count="X|Y" but I can't be more precise than that without knowing (a) what your XML source looks like, and (b) what the context item is at the point where your named templates are called.

Upvotes: 1

Related Questions