Adding Automatic Numbering

 
xslt for dummies
Chapter 9 - Tweaking the Results to Get What You Want
XSLT For Dummies
by Richard Wagner
Hungry Minds 2002
  

People frequently need to automatically generate sequential numbers for tree nodes in order to create the results documents they want. My earlier examples are cases in point: I use ordinary asterisks before each list item to form a bulleted list. But suppose I want to get fancy and change the bulleted list to a numbered list. Consider the same XML snippet as the source:

 <beverages> <beverage>Coffee</beverage> <beverage>Tea</beverage> <beverage>Milk</beverage> <beverage>Cola</beverage> <beverage>Diet Cola</beverage> <beverage>Root Beer</beverage> <beverage>Water</beverage> <beverage>Lemonade</beverage> <beverage>Iced Tea</beverage> <beverage>Wine</beverage> </beverages> 

The xsl:number element is used to generate a unique number for each item and write a formatted number to the result tree. For a basic list, I use just the instruction without any attributes:

 <xsl:template match="beverage"> <xsl:number/>. <xsl:apply-templates/> </xsl:template> 

The resulting output is:

 1. Coffee 2. Tea 3. Milk 4. Cola 5. Diet Cola 6. Root Beer 7. Water 8. Lemonade 9. Iced Tea 10. Wine 

 Tip   When you use xsl:number without any of its formatting attributes, the result is simply a number. Therefore, I added a . (period and space) as literal text between the xsl:number and xsl:value-of instructions to generate a formatted list. However, I show you in the next section how you can add those literal text formatting characters inside the xsl:number definition.

Adjusting the format

The default number format is a simple 1, 2, 3, and so on until the end of the nodes. However, xsl:number provides an optional format attribute to provide a kind of numbering template. The XSLT processor looks at the format attribute value and bases its auto-numbering output on it. There are three basic types of tokens:

  • Numbers (1, 2, 3, and so on)

  • Roman numerals (I, II, III, and so on or i, ii, iii, and so on)

  • Letters (A, B, C, and so on or a, b, c, and so on)

You can intermix a token with literal text to create a template that you want reproduced for each xsl:number instruction processed . For example:

 <xsl:number format="1. "/> 

This format value has the 1 token along with literal text (a period and a space), so that when the instruction is converted to a string, a unique sequential number plus the literal text is added to the output.

Table 9-1 lists a variety of common format values and the sequences for each one.

Table 9-1: Number Formats

format Value

Sequence

Example Output

format="1"

1, 2, 3 10, 11, 12 101, 102, 103

1Casablanca

format="01"

01, 02, 03 10, 11, 12 101, 102, 103

01Casablanca

format="001"

001, 002, 003 010, 011, 012 101, 102, 103

001Casablanca

format="1. "

1, 2, 3 10, 11, 12 101, 102, 103

1. Casablanca

format="(1) "

1, 2, 3 10, 11, 12 101, 102, 103

(1) Casablanca

format="I "

I, II, III, IV

I Casablanca

format="i "

i, ii, iii, iv

i Casablanca

format="i) "

i, ii, iii, iv

i) Casablanca

format="A "

A, B, C Z, AA, BB, CC

A Casablanca

format="a "

a, b, c z, aa, bb, cc

a Casablanca

To illustrate , suppose you add a format attribute to the xsl:number element in the preceding example:

 <xsl:template match="beverage"> <xsl:number format="(01) "/> <xsl:apply-templates/> </xsl:template> 

The output is then changed to:

 (01) Coffee (02) Tea (03) Milk (04) Cola (05) Diet Cola (06) Root Beer (07) Water (08) Lemonade (09) Iced Tea (10) Wine 

 Tip   If you are numbering a result document that can count into the thousands or even millions, you can use xsl:number s grouping- size and grouping-separator attributes to add grouping formatting:

  • grouping-size="number" specifies the size of a group of digits. This value is normally 3.

  • grouping-separator="character" defines the character used to separate the number groups. This value is normally a comma.

For example, if you used grouping-size="3" and grouping-separator= ",", you get the following values: 1,000 and 1,000,000 .

Handling multiple levels

The xsl:number instruction provides a level attribute to enable you to handle numbering in a result document that needs numbering at multiple levels. For example, suppose I add a second level of beverage elements to the beverages structure:

 <beverages> <beverage>Coffee <beverage>Drip Coffee</beverage> <beverage>Latte</beverage> <beverage>Espresso</beverage> <beverage>Cappuccino</beverage> </beverage> <beverage>Tea <beverage>Earl Grey</beverage> <beverage>Green Tea</beverage> </beverage> <beverage>Milk <beverage>Skim Milk</beverage> <beverage>2 Percent Milk</beverage> <beverage>Whole Milk</beverage> </beverage> <beverage>Cola <beverage>Coca Cola</beverage> <beverage>Pepsi</beverage> <beverage>RC</beverage> </beverage> <beverage>Diet Cola <beverage>Diet Coke</beverage> <beverage>Diet Pepsi</beverage> </beverage> <beverage>Root Beer</beverage> <beverage>Water <beverage>Sparking Mineral Water</beverage> <beverage>Spring Water</beverage> </beverage> <beverage>Lemonade</beverage> <beverage>Iced Tea</beverage> <beverage>Wine</beverage> </beverages> 

Using xsl:number s level attribute, I can specify how I want to number the result document across these levels. The following template rule shows the level attribute being added:

 <xsl:template match="beverage"> <xsl:number format="1. " level="single"/> <xsl:apply-templates/> </xsl:template> 

The level attribute accepts three values:

  • level="single" (the default) numbers each level on its own, ignoring other levels that may exist above it or below it. The resulting output is:

 1. Coffee 1. Drip Coffee 2. Latte 3. Espresso 4. Cappuccino 2. Tea 1. Earl Grey 2. Green Tea 3. Milk 1. Skim Milk 2. 2 Percent Milk 3. Whole Milk 4. Cola 1. Coca Cola 2. Pepsi 3. RC 5. Diet Cola 1. Diet Coke 2. Diet Pepsi 6. Root Beer 7. Water 1. Sparking Mineral Water 2. Spring Water 8. Lemonade 9. Iced Tea 10. Wine 
  • level="multiple" treats the document as something like an outline, maintaining an interrelationship across hierarchical levels:

 1. Coffee 1.1. Drip Coffee 1.2. Latte 1.3. Espresso 1.4. Cappuccino 2. Tea 2.1. Earl Grey 2.2. Green Tea 3. Milk 3.1. Skim Milk 3.2. 2 Percent Milk 3.3. Whole Milk 4. Cola 4.1. Coca Cola 4.2. Pepsi 4.3. RC 5. Diet Cola 5.1. Diet Coke 5.2. Diet Pepsi 6. Root Beer 7. Water 7.1. Sparking Mineral Water 7.2. Spring Water 8. Lemonade 9. Iced Tea 10. Wine 
  • level="any" ignores hierarchy altogether and simply numbers each matching node sequentially:

 1. Coffee 2. Drip Coffee 3. Latte 4. Espresso 5. Cappuccino 6. Tea 7. Earl Grey 8. Green Tea 9. Milk 10. Skim Milk 11. 2 Percent Milk 12. Whole Milk 13. Cola 14. Coca Cola 15. Pepsi 16. RC 17. Diet Cola 18. Diet Coke 19. Diet Pepsi 20. Root Beer 21. Water 22. Sparking Mineral Water 23. Spring Water 24. Lemonade 25. Iced Tea 26. Wine 

By default, the XSLT processor uses all the nodes it encounters that match the sort key in determining the list count. So, in the earlier template rule examples that returned beverage element nodes, the numbering scheme is based on this type of element. However, XSLT allows you to base the count on more than just the selected node by using its count attribute. The xsl:number s count attribute specifies the nodes that need to be counted at the levels defined by the level attribute.

I illustrate the usage of the count attribute by adding a size element to the beverages document:

 <beverages> <beverage>Coffee <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> <beverage>Tea <size>Small</size> <size>Large</size> </beverage> <beverage>Milk <size>Small</size> <size>Large</size> </beverage> <beverage>Cola <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> <beverage>Diet Cola <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> </beverages> 

My objective is to number the result document like an outline (1., 1.1, 1.2, 2., 2.1, and so on), so I first add a level="multiple" attribute value. I want to base the first part of my size number on the position of the parent beverage element.

To do so, I am going to create two template rules: the first to number the beverage elements like I did before, and the second to number the size elements. In the size template rule, I add the count attribute to specify that the first number in a multilevel number is based on the number of the beverage element, while the second value is based on the size element:

 <xsl:template match="beverage"> <xsl:number format="1. " level="multiple"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="size"> <xsl:number format="1. " level="multiple" count="beveragesize"/> <xsl:apply-templates/> </xsl:template> 

The result of this transformation is as follows :

 1. Coffee 1.1. Small 1.2. Medium 1.3. Large 1.4. Extra Large 2. Tea 2.1. Small 2.2. Large 3. Milk 3.1. Small 3.2. Large 4. Cola 4.1. Small 4.2. Medium 4.3. Large 4.4. Extra Large 5. Diet Cola 5.1. Small 5.2. Medium 5.3. Large 5.4. Extra Large 

The following example shows how you can use count to span your sequencing across multiple levels and elements. By adding a types element to the beverages structure, the following XML snippet becomes the source:

 <beverages> <types>Starbucks Crowd <beverage>Coffee <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> <beverage>Tea <size>Small</size> <size>Large</size> </beverage> </types> <types>Natural Types <beverage>Milk <size>Small</size> <size>Large</size> </beverage> <beverage>Water <size>Small</size> <size>Medium</size> <size>Large</size> </beverage> </types> <types>Popular Sodas <beverage>Cola <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> <beverage>Diet Cola <size>Small</size> <size>Medium</size> <size>Large</size> <size>Extra Large</size> </beverage> <beverage>Root Beer <size>Medium</size> </beverage> </types> </beverages> 

To use the number of the types element in the outline numbering scheme of the beverage and size elements, add the types element to the count attribute value:

 <xsl:template match="beverage"> <xsl:number format="1. " level="multiple" count="typesbeverage"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="size"> <xsl:number format="1. " level="multiple" count="typesbeveragesize"/> <xsl:apply-templates/> </xsl:template> 

The result is as follows:

 Starbucks Crowd 1.1. Coffee 1.1.1. Small 1.1.2. Medium 1.1.3. Large 1.1.4. Extra Large 1.2. Tea 1.2.1. Small 1.2.2. Large Natural Types 2.1. Milk 2.1.1. Small 2.1.2. Large 2.2. Water 2.2.1. Small 2.2.2. Medium 2.2.3. Large Popular Sodas 3.1. Cola 3.1.1. Small 3.1.2. Medium 3.1.3. Large 3.1.4. Extra Large 3.2. Diet Cola 3.2.1. Small 3.2.2. Medium 3.2.3. Large 3.2.4. Extra Large 3.3. Root Beer 3.3.1. Medium 

In this example, the types elements arent numbered, but their index value is factored into the overall number sequence of the beverage and size elements.

  
 
 
2000-2002    Feedback


XSLT For Dummies
XSLT for Dummies
ISBN: 0764536516
EAN: 2147483647
Year: 2002
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net