Formatting Decimal Numbers
We’ve already seen several ways of formatting decimal
numbers using <xsl:number>
.
However, if we’re going to work with numbers, we’ll almost certainly
have to deal with decimals. XSLT defines the format-number()
function and the <xsl:decimal-format>
element to do just that. We’ll use <xsl:decimal-format>
to define a pattern
for formatting numbers, and then we’ll use format-number()
to apply a pattern to a
number.
This stylesheet has several examples of <xsl:decimal-format>
and format-number()
:
<?xml version="1.0" encoding="utf-8"?>
<!-- decimal-format.xsl -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- This format has no name, so it's assumed to be the default. -->
<xsl:decimal-format decimal-separator="," grouping-separator="."/>
<xsl:decimal-format name="us_default"/>
<xsl:decimal-format name="other_options" NaN="[not a number]"
infinity="unfathomably huge"/>
<xsl:decimal-format name="hash_mark" digit="!"/>
<xsl:template match="/">
<xsl:text>
Tests of <xsl:decimal-format> and </xsl:text>
<xsl:text>format-number():</xsl:text>
<xsl:text>

 1. format-number(3728493.3882, </xsl:text>
<xsl:text>'#.###,##') : </xsl:text>
<xsl:value-of
select="format-number(3728493.3882, '#.###,##')"/>
<xsl:text>

 2. format-number(3728493.3882, </xsl:text>
<xsl:text>'#,###.##', 'us_default') : </xsl:text>
<xsl:value-of
select="format-number(3728493.3882, '#,###.##', 'us_default')"/>
<xsl:text>

 3. format-number(number(1) div 0, '#.#') : </xsl:text>
<xsl:value-of select="format-number(number(1) div 0, '#.#')"/>
<xsl:text>

 4. format-number(number(1) div 0, '#.#', </xsl:text>
<xsl:text>'other_options') : 
 </xsl:text>
<xsl:value-of
select="format-number(number(1) div 0, '#.#', 'other_options')"/>
<xsl:text>

 5. format-number(number('blue') * </xsl:text>
<xsl:text>number('orange'), '#') : </xsl:text>
<xsl:value-of
select="format-number(number('blue') * number('orange'), '#')"/>
<xsl:text>

 6. format-number(number('blue') * </xsl:text>
<xsl:text>number('orange'), '#', 'other_options') : </xsl:text>
<xsl:text>
 </xsl:text>
<xsl:value-of
select="format-number(number('blue') * number('orange'), '#',
'other_options')"/>
<xsl:text>

 7. format-number(42, '#!', </xsl:text>
<xsl:text>'hash_mark') : </xsl:text>
<xsl:value-of select="format-number(42, '#!', 'hash_mark')"/>
</xsl:template>
</xsl:stylesheet>
When we run this stylesheet against any document, we get this output:
Tests of <xsl:decimal-format> and format-number(): 1. format-number(3728493.3882, '#.###,##') : 3.728.493,39 2. format-number(3728493.3882, '#,###.##', 'us_default') : 3,728,493.39 3. format-number(number(1) div 0, '#.#') : Infinity 4. format-number(number(1) div 0, '#.#', 'other_options') : unfathomably huge 5. format-number(number('blue') * number('orange'), '#') : NaN 6. format-number(number('blue') * number('orange'), '#', 'other_options') : [not a number] 7. format-number(42, '#!', 'hash_mark') : #42
This is an XSLT 1.0 stylesheet, but changing the version
attribute to 2.0
generates the same results. We’ll discuss
each line in the output and point out some differences in the way XSLT
2.0 processes numbers as we go.
This example formats a number using the default format we defined. Because our stylesheet has a
<xsl:decimal-format>
element without a name, this format is used unless the name of another<xsl:decimal-format>
element is used. We defined the default format to use periods as the thousands separator and a comma as the decimal point. Notice that to get this to work we had to use the period and comma appropriately in the formatting string. The numeric value itself uses the decimal point as usual.This is the same example as before, only we pass the name of a number format as the third argument. Notice that the decimal format
us_default
doesn’t have any attributes; that means it uses a period as the decimal point and a comma as the thousands separator.This generates the default value for infinity, which is the string
Infinity
. In XSLT 1.0, the expression1 div 0
generates the same result.[2.0] In XSLT 2.0, any value that is an
xs:integer
orxs:decimal
cannot have the value infinity, so1 div 0
won’t run at all. Calling the functionnumber(1)
converts1
into thexs:double
equivalent, sonumber(1) div 0
works. (That’s a lot of work to divide a number by zero, but it does explain some of the details of how math works in XSLT 2.0.)This generates the value for infinity defined in the number format
other_options
. The same details apply here as in the previous example; the expression1 div 0
doesn’t work in XSLT 2.0.This generates
NaN
. In XSLT 1.0, the expression'blue' * 'orange'
generates the same result.[2.0] In XSLT 2.0, the expression
'blue' * 'orange'
doesn’t work because the multiplication symbol requires two numbers. Using thenumber()
function turns each of the strings into the numeric valueNaN
. To generateNaN
in XSLT 2.0,number('NaN')
does the trick, as donumber('blue')
,number('orange')
, andnumber('any old string at all')
.This generates
[not a number]
, the value defined in the number formatother_options
. (The same restrictions for XSLT 2.0 apply here as well.)This generates
#42
. This uses the number formathash_mark
, which defines an exclamation point as the digit character in the picture string. This allows us to put the hash mark into the picture string.
Get XSLT, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.