![]() |
Table of Contents |
![]() |
Recipe 3.1. Formatting NumbersProblemYou need to display numbers in various formats. SolutionThis problem has two general solutions. Use xsl:decimal-format in conjunction with format-number( )The top-level element xsl:decimal-format establishes a named formatting rule that can be referenced by the format-number( ) function whenever that format rule is required. xsl:decimal-format has a rich set of attributes that describe the formatting rules. Table 3-1 explains each attribute and shows its default value in parentheses.
The format-number( ) function takes the arguments shown in Table 3-2.
Use xsl:numberThe most common use of xsl:number is to number nodes sequentially. However, it can also format numbers. When used to perform the later, the relevant attributes are shown in Table 3-3.
Table 3-4 shows how formatting tokens are used with a format attribute.
The format string is an alternating sequence of format and punctuation tokens. Using multiple format tokens makes sense only when the value contains a set of numbers. DiscussionGiven the formatting machinery defined earlier, almost any numeric-formatting task can be handled. Formatting numbers into columns using a fixed number of decimal placesHere we can take advantage of leading and trailing zero padding and then map the leading zeros to spaces and use a trailing minus sign. This solution gives a nice columnar, right-justified output when the final display medium uses a fixed-width font. Example 3-1 through Example 3-3 show more conventional ways of padding. The examples illustrate the behavior of the 0 digit when used as a format character. Example 3-1. Input<numbers> <number>10</number> <number>3.5</number> <number>4.44</number> <number>77.7777</number> <number>-8</number> <number>1</number> <number>444</number> <number>1.1234</number> <number>7.77</number> <number>3.1415927</number> <number>10</number> <number>9</number> <number>8</number> <number>7</number> <number>666</number> <number>5555</number> <number>-4444444</number> <number>22.33</number> <number>18</number> <number>36.54</number> <number>43</number> <number>99999</number> <number>999999</number> <number>9999999</number> <number>32</number> <number>64</number> <number>-64.0001</number> </numbers> Example 3-2. format-numbers-into-columns.xslt<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" /> <xsl:variable name="numCols" select="3"/> <xsl:template match="numbers"> <xsl:for-each select="number[position( ) mod $numCols = 1]"> <xsl:apply-templates select=". | following-sibling::number[position( ) < $numCols]" mode="format"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template match="number" name="format" mode="format"> <xsl:param name="number" select="." /> <xsl:call-template name="leading-zero-to-space"> <xsl:with-param name="input" select="format-number($number, '0000000.0000 ;0000000.0000- ')"/> </xsl:call-template> </xsl:template> <xsl:template name="leading-zero-to-space"> <xsl:param name="input"/> <xsl:choose> <xsl:when test="starts-with($input,'0')"> <xsl:value-of select="' '"/> <xsl:call-template name="leading-zero-to-space"> <xsl:with-param name="input" select="substring-after($input,'0')"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$input"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> Example 3-3. Output10.0000 3.5000 4.4400 77.7777 8.0000- 1.0000 444.0000 1.1234 7.7700 3.1416 10.0000 9.0000 8.0000 7.0000 666.0000 5555.0000 4444444.0000- 22.3300 18.0000 36.5400 43.0000 99999.0000 999999.0000 9999999.0000 32.0000 64.0000 64.0001- Formatting money like U.S. accountantsExample 3-4 gives a variation of the previous format template that will make your accountant happy. Example 3-4. Accountant-friendly format<xsl:template match="number" name="format" mode="format"> <xsl:param name="number" select="." /> <xsl:text> $ </xsl:text> <xsl:call-template name="leading-zero-to-space"> <xsl:with-param name="input" select="format-number($number, ' 0000000.00 ;(0000000.00)')"/> </xsl:call-template> </xsl:template> Output: $ 10.00 $ 3.50 $ 4.44 $ 77.78 $ (8.00) $ 1.00 $ 444.00 $ 1.12 $ 7.77 $ 3.14 $ 10.00 $ 9.00 $ 8.00 $ 7.00 $ 666.00 $ 5555.00 $ (4444444.00) $ 22.33 $ 18.00 $ 36.54 $ 43.00 $ 99999.00 $ 999999.00 $ 9999999.00 $ 32.00 $ 64.00 $ (64.00) Formatting numbers for many European countriesExample 3-5 demonstrates the use of a named format. Example 3-5. European-number format<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings"> <xsl:output method="text" /> <!-- From Recipe 2.5 ... --> <xsl:include href="../strings/str.dup.xslt"/> <xsl:variable name="numCols" select="3"/> <xsl:decimal-format name="WesternEurope" decimal-separator="," grouping-separator="."/> <xsl:template match="numbers"> <xsl:for-each select="number[position( ) mod $numCols = 1]"> <xsl:apply-templates select=". | following-sibling::number[position( ) < $numCols]" mode="format"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template match="number" name="format" mode="format"> <xsl:param name="number" select="." /> <xsl:call-template name="pad"> <xsl:with-param name="string" select="format-number($number,'#.###,00','WesternEurope')"/> </xsl:call-template> </xsl:template> <xsl:template name="pad"> <xsl:param name="string"/> <xsl:param name="width" select="16"/> <xsl:call-template name="str:dup"> <xsl:with-param name="input" select="' '"/> <xsl:with-param name="count" select="$width - string-length($string)"/> </xsl:call-template> <xsl:value-of select="$string"/> </xsl:template> </xsl:stylesheet> Output: 10,00 3,50 4,44 77,78 -8,00 1,00 444,00 1,12 7,77 3,14 10,00 9,00 8,00 7,00 666,00 5.555,00 -4.444.444,00 22,33 18,00 36,54 43,00 99.999,00 999.999,00 9.999.999,00 32,00 64,00 -64,00 Converting numbers to Roman numeralsExample 3-6 uses xsl:number as a Roman numeral formatter to label columns as rows: Example 3-6. Roman-numeral format<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings"> <xsl:output method="text" /> <xsl:include href="../strings/str.dup.xslt"/> <xsl:variable name="numCols" select="3"/> <xsl:template match="numbers"> <xsl:for-each select="number[position() <= $numCols]"> <xsl:text> </xsl:text> <xsl:number value="position()" format="I"/><xsl:text> </xsl:text> </xsl:for-each> <xsl:text>
 </xsl:text> <xsl:for-each select="number[position() <= $numCols]"> <xsl:text>---------------- </xsl:text> </xsl:for-each> <xsl:text>
</xsl:text> <xsl:for-each select="number[position( ) mod $numCols = 1]"> <xsl:call-template name="pad"> <xsl:with-param name="string"> <xsl:number value="position()" format="i"/> </xsl:with-param> <xsl:with-param name="width" select="4"/> </xsl:call-template>|<xsl:text/> <!-- See recipe 7.1--> <xsl:apply-templates select=". | following-sibling::number[position( ) < $numCols]" mode="format"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template match="number" name="format" mode="format"> <xsl:param name="number" select="." /> <xsl:call-template name="pad"> <xsl:with-param name="string" select="format-number(.,'#,###.00')"/> </xsl:call-template> </xsl:template> <xsl:template name="pad"> <xsl:param name="string"/> <xsl:param name="width" select="16"/> <xsl:call-template name="str:dup"> <xsl:with-param name="input" select="' '"/> <xsl:with-param name="count" select="$width - string-length($string)"/> </xsl:call-template> <xsl:value-of select="$string"/> </xsl:template> </xsl:stylesheet> Output: I II III ---------------- -------------- -------------- i| 10.00 3.50 4.44 ii| 77.78 -8.00 1.00 iii| 444.00 1.12 7.77 iv| 3.14 10.00 9.00 v| 8.00 7.00 666.00 vi| 5,555.00 -4,444,444.00 22.33 vii| 18.00 36.54 43.00 viii| 99,999.00 999,999.00 9,999,999.00 ix| 32.00 64.00 -64.00 Creating column numbers like a spreadsheetSpreadsheets number columns in the alpha sequence A, B, C ... ZZ, and we can do the same using xsl:number (see Example 3-7). Example 3-7. Spreadsheet-like column numbers<xsl:template match="numbers"> <xsl:for-each select="number[position( ) <= $numCols]"> <xsl:text> </xsl:text> <xsl:number value="position()" format="A"/><xsl:text> </xsl:text> <xsl:text> </xsl:text> </xsl:for-each> <xsl:text>
</xsl:text> <xsl:for-each select="number[position( ) <= $numCols]"> <xsl:text> ---------------- </xsl:text> </xsl:for-each> <xsl:text>
</xsl:text> <xsl:for-each select="number[position( ) mod $numCols = 1]"> <xsl:value-of select="position()"/><xsl:text>|</xsl:text> <xsl:apply-templates select=". | following-sibling::number[position( ) < $numCols]" mode="format"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template match="number" name="format" mode="format"> <xsl:param name="number" select="." /> <xsl:call-template name="pad"> <xsl:with-param name="string" select="format-number(.,'#,###.00')"/> </xsl:call-template> <xsl:text> </xsl:text> </xsl:template> Output: A B C ------------ ------------ ------------ 1| 10.0000 3.5000 4.4400 2| 77.7777 8.0000- 1.0000 3| 444.0000 1.1234 7.7700 4| 3.1416 10.0000 9.0000 5| 8.0000 7.0000 666.0000 6| 5555.0000 4444444.0000- 22.3300 7| 18.0000 36.5400 43.0000 8| 99999.0000 999999.0000 9999999.0000 9| 32.0000 64.0000 64.0001- Formatting numbers using Arabic charactersNumeric characters from other languages can be used by setting the zero digit in xsl:decimal-format to the zero of that language. This example uses the Unicode character 0x660 (Arabic-Indic Digit Zero): <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings"> <xsl:output method="text" encoding="UTF-8"/> <xsl:include href="../strings/str.dup.xslt"/> <!-- This states that zero starts at character 0x660, which implies one is 0x661, etc. --> <xsl:decimal-format name="Arabic" zero-digit="٠"/> <xsl:template match="numbers"> <xsl:for-each select="number"> <xsl:call-template name="pad"> <xsl:with-param name="string" select="format-number(.,'#,###.00')"/> </xsl:call-template> = <xsl:text/> <xsl:value-of select="format-number(.,'#,###.٠٠','Arabic')"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> <xsl:template name="pad"> <xsl:param name="string"/> <xsl:param name="width" select="16"/> <xsl:value-of select="$string"/> <xsl:call-template name="str:dup"> <xsl:with-param name="input" select="' '"/> <xsl:with-param name="count" select="$width - string-length($string)"/> </xsl:call-template> </xsl:template> </xsl:stylesheet> Here is the output of this code: ![]() |
![]() |
Table of Contents |
![]() |