Since output can be numbered in many ways, this example presents a series of increasingly complex examples that address the most common (and a few uncommon) numbering needs.
This category is the simplest form of numbering. For example, you can produce a numbered list of people using the stylesheet in Example 5-35 and Example 5-36.
Example 5-35. Stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="person"> <xsl:number count="*" format="1. "/> <xsl:value-of select="@name"/> </xsl:template> </xsl:stylesheet>
Example 5-36. Output
1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Willimas 5. Edward Ulster 6. Frank Townsend 7. Greg Sutter 8. Harry Rogers 9. John Quincy 10. Kent Peterson ...
You can use the justify template discussed in Recipe 5.3 if you want right-justified numbers.
xsl:number
does not provide a standard facility for
starting from or incrementing by a number other than one, but you can
handle this task with a little math. Example 5-37 and
Example 5-38 start from ten and increment by five,
just to be different.
Example 5-37. Stylesheet using nonsequential numbering
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="person"> <xsl:variable name="num"> <xsl:number count="*"/> </xsl:variable> <xsl:number value="($num - 1) * 5 + 10" format="1. "/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
Example 5-38. Output
10. Al Zehtooney 15. Brad York 20. Charles Xavier 25. David Willimas 30. Edward Ulster 35. Frank Townsend 40. Greg Sutter 45. Harry Rogers 50. John Quincy 55. Kent Peterson ...
This scenario works even if you want
the final output to use a non-numerical format. For example, Example 5-39 and Example 5-40 use the same
technique to start numbering at L
.
Example 5-39. Stylesheet for numbering from L
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="person"> <xsl:variable name="num"> <xsl:number count="*"/> </xsl:variable> <xsl:number value="$num + 11" format="A. "/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
Sometimes you want to number elements sequentially without regard to their context. The most common example involves a document that contains footnote elements. The footnotes can appear at any level in the document’s structure, yet they should be numbered sequentially. However, to continue the theme of your example, here is a document that divides people into various groups and subgroups:
<people> <group> <person name="Al Zehtooney" age="33" sex="m" smoker="no"/> <person name="Brad York" age="38" sex="m" smoker="yes"/> <person name="Charles Xavier" age="32" sex="m" smoker="no"/> <person name="David Willimas" age="33" sex="m" smoker="no"/> <person name="Edward Ulster" age="33" sex="m" smoker="yes"/> <person name="Frank Townsend" age="35" sex="m" smoker="no"/> </group> <group> <person name="Greg Sutter" age="40" sex="m" smoker="no"/> <person name="Harry Rogers" age="37" sex="m" smoker="no"/> <group> <person name="John Quincy" age="43" sex="m" smoker="yes"/> <person name="Kent Peterson" age="31" sex="m" smoker="no"/> <person name="Larry Newell" age="23" sex="m" smoker="no"/> <group> <person name="Max Milton" age="22" sex="m" smoker="no"/> <person name="Norman Lamagna" age="30" sex="m" smoker="no"/> <person name="Ollie Kensinton" age="44" sex="m" smoker="no"/> </group> <person name="John Frank" age="24" sex="m" smoker="no"/> </group> <group> <person name="Mary Williams" age="33" sex="f" smoker="no"/> <person name="Jane Frank" age="38" sex="f" smoker="yes"/> <person name="Jo Peterson" age="32" sex="f" smoker="no"/> <person name="Angie Frost" age="33" sex="f" smoker="no"/> <person name="Betty Bates" age="33" sex="f" smoker="no"/> <person name="Connie Date" age="35" sex="f" smoker="no"/> <person name="Donna Finster" age="20" sex="f" smoker="no"/> </group> <group> <person name="Esther Gates" age="37" sex="f" smoker="no"/> <person name="Fanny Hill" age="33" sex="f" smoker="yes"/> <person name="Geta Iota" age="27" sex="f" smoker="no"/> <person name="Hillary Johnson" age="22" sex="f" smoker="no"/> <person name="Ingrid Kent" age="21" sex="f" smoker="no"/> <person name="Jill Larson" age="20" sex="f" smoker="no"/> <person name="Kim Mulrooney" age="41" sex="f" smoker="no"/> <person name="Lisa Nevins" age="21" sex="f" smoker="no"/> </group> </group> </people>
The only necessary change is to use the xsl:number
attribute level="any"
. This attribute instructs
the XSLT processor to consider all preceding occurrences of the
person element when determining numbering. See Example 5-41 and Example 5-42.
Example 5-41. Stylesheet for level="any”
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="person"> <xsl:number count="person" level="any" format="1. "/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
Example 5-42. Output with level="any”
1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Willimas 5. Edward Ulster 6. Frank Townsend 7. Greg Sutter 8. Harry Rogers 9. John Quincy 10. Kent Peterson 11. Larry Newell 12. Max Milton 13. Norman Lamagna 14. Ollie Kensinton 15. John Frank 16. Mary Williams 17. Jane Frank 18. Jo Peterson 19. Angie Frost 20. Betty Bates 21. Connie Date 22. Donna Finster 23. Esther Gates 24. Fanny Hill 25. Geta Iota 26. Hillary Johnson 27. Ingrid Kent 28. Jill Larson 29. Kim Mulrooney 30. Lisa Nevins
Sometimes you want to restrict global numbering to a specific context. For example, suppose you want to number people within their top-level group and ignore subgroups:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="people/group"> <xsl:text>Group </xsl:text> <xsl:number count="group"/> <xsl:text>
</xsl:text> <xsl:apply-templates/> <xsl:text>
</xsl:text> </xsl:template> <xsl:template match="person"> <xsl:number count="person" level="any" from="people/group" format="1. "/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet> Group 1 1. Al Zehtooney 2. Brad York 3. Charles Xavier 4. David Willimas 5. Edward Ulster 6. Frank Townsend Group 2 1. Greg Sutter 2. Harry Rogers 3. John Quincy 4. Kent Peterson 5. Larry Newell 6. Max Milton 7. Norman Lamagna 8. Ollie Kensinton 9. John Frank 10. Mary Williams 11. Jane Frank 12. Jo Peterson 13. Angie Frost 14. Betty Bates 15. Connie Date 16. Donna Finster 17. Esther Gates 18. Fanny Hill 19. Geta Iota 20. Hillary Johnson 21. Ingrid Kent 22. Jill Larson 23. Kim Mulrooney 24. Lisa Nevins
In
formal and legal documents, items are often numbered based on both
their sequence and level within a hierarchy. As shown in Example 5-43 to Example 5-45,
xsl:number
supports this via attribute
level="multiple"
.
Example 5-43. Hierarchical numbering based on group and person
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="people/group"> <xsl:text>Group </xsl:text> <xsl:number count="group"/> <xsl:text>
</xsl:text> <xsl:apply-templates/> <xsl:text>
</xsl:text> </xsl:template> <xsl:template match="person"> <xsl:number count="group | person" level="multiple" format="1.1.1 "/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
The numbering achieved by the stylesheet in Example 5-45 is somewhat odd, but it effectively
illustrates the effect of attribute count
when it
is used with level =
"multiple"
. The count attribute is simply a
specification for determining what ancestor elements should be
included when composing a hierarchical number. The stylesheet
assigned numbers to people based on group or person elements. Bard
York is assigned 1.2 because he is in Group 1 and is the second
person in the group. Likewise, Max Milton is assigned 2.3.4.1 because
he is in Group 2 when considering only top-level groups; he is in
Group 3 when considering both top- and
second-level groups; he is in Group 4 when considering all top-,
second-, and third-level groups; and he is the first person within
his own group:
Group 1 1.1 Al Zehtooney 1.2 Brad York 1.3 Charles Xavier 1.4 David Willimas 1.5 Edward Ulster 1.6 Frank Townsend Group 2 2.1 Greg Sutter 2.2 Harry Rogers 2.3.1 John Quincy 2.3.2 Kent Peterson 2.3.3 Larry Newell 2.3.4.1 Max Milton 2.3.4.2 Norman Lamagna 2.3.4.3 Ollie Kensinton 2.3.5 John Frank 2.4.1 Mary Williams 2.4.2 Jane Frank 2.4.3 Jo Peterson 2.4.4 Angie Frost 2.4.5 Betty Bates 2.4.6 Connie Date 2.4.7 Donna Finster 2.5.1 Esther Gates 2.5.2 Fanny Hill 2.5.3 Geta Iota 2.5.4 Hillary Johnson 2.5.5 Ingrid Kent 2.5.6 Jill Larson 2.5.7 Kim Mulrooney 2.5.8 Lisa Nevins
In typical applications, you expect a
numbering scheme in which the number at any level is
relative to the number at the next higher level.
You can achieve this relationship by using multiple and adjacent
xsl:number
elements, as shown in Example 5-44 and
5-45.
Example 5-44. Stylesheet for creating muliple ordered levels
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="group">
<xsl:text>Group </xsl:text>
<xsl:number count="group" level="multiple"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="person">
<xsl:number count="group" level="multiple" format="1.1.1."/>
<xsl:number count="person" level="single" format="1 "/>
<xsl:value-of select="@name"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Example 5-45. Output
Group 1 1.1 Al Zehtooney 1.2 Brad York 1.3 Charles Xavier 1.4 David Willimas 1.5 Edward Ulster 1.6 Frank Townsend Group 2 2.1 Greg Sutter 2.2 Harry Rogers Group 2.1 2.1.1 John Quincy 2.1.2 Kent Peterson 2.1.3 Larry Newell Group 2.1.1 2.1.1.1 Max Milton 2.1.1.2 Norman Lamagna 2.1.1.3 Ollie Kensinton 2.1.4 John Frank Group 2.2 2.2.1 Mary Williams 2.2.2 Jane Frank 2.2.3 Jo Peterson 2.2.4 Angie Frost 2.2.5 Betty Bates 2.2.6 Connie Date 2.2.7 Donna Finster Group 2.3 2.3.1 Esther Gates 2.3.2 Fanny Hill 2.3.3 Geta Iota 2.3.4 Hillary Johnson 2.3.5 Ingrid Kent 2.3.6 Jill Larson 2.3.7 Kim Mulrooney 2.3.8 Lisa Nevins
Almost any numbering scheme is realizable by using one or more
xsl:number
elements with the appropriate attribute
settings. However, extensive use of xsl:number
(especially with level="multiple"
) can slow down
your stylesheets. With very deeply nested hierarchical numbers, you
can achieve a performance boost by passing the parent-level numbering
down to the children via a parameter. Notice how you can achieve a
hierarchal numbering in this fashion without using
xsl:number
at all:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="group"><xsl:param name="parent-level" select=" '' "/>
<xsl:variable name="number" select="concat($parent-level,position( ))"/>
<xsl:text>Group </xsl:text> <xsl:value-of select="$number"/> <xsl:text>
</xsl:text> <xsl:apply-templates> <xsl:with-param name="parent-level" select="concat($number,'.')"/> </xsl:apply-templates> </xsl:template> <xsl:template match="person"><xsl:param name="parent-level" select=" '' "/>
<xsl:variable name="number">
<xsl:value-of select="concat($parent-level,position( ),' ')"/>
</xsl:variable>
<xsl:value-of select="$number"/> <xsl:value-of select="@name"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
This use of position is less convenient when the numbering scheme requires letters for roman numerals.