469,282 Members | 2,034 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,282 developers. It's quick & easy.

XSLT: Generating a format mask string based on the content of a node?

I want to use the format-number function to output
a number with a specific number of decimals, based on the content
of a node... Is there any smart way to generate differnet strings
based on a node-value?

if decimals = 0, I want "###,###"
decimals = 1, I want "###,###.0"
decimals = 2, I want "###,###.00"
decimals = n, I want "###,###.000..n"

Today I'm using <xsl:choose>, but that is limited to
hand-coding the alternatives.

I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Transform.xslt"?>
<root>
<valuedef>
<value>7</value>
<decimals>2</decimals>
</valuedef>
<valuedef>
<value>42</value>
<decimals>3</decimals>
</valuedef>
</root>

And I transform it like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
<xsl:apply-templates select="/root/valuedef" />
</xsl:template>

<xsl:template match="valuedef">
<value>
<xsl:variable name="tall" select="value"/>
<xsl:variable name="maske">
<xsl:choose>
<xsl:when test="decimals[.='0']"###,### </xsl:when>
<xsl:when test="decimals[.='1']"###,###.0</xsl:when>
<xsl:when test="decimals[.='2']"###,###.00</xsl:when>
<xsl:when test="decimals[.='3']"###,###.000</xsl:when>
<xsl:otherwise###,### </xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select='format-number($tall, $maske)' />
</value>
</xsl:template>
</xsl:stylesheet>

TIA...

--
Dag.
Mar 9 '07 #1
7 8215
Your question is really that you want a better solution for
<xsl:choose>
<xsl:when test="decimals[.='0']"###,### </xsl:when>
<xsl:when test="decimals[.='1']"###,###.0</xsl:when>
<xsl:when test="decimals[.='2']"###,###.00</xsl:when>
<xsl:when test="decimals[.='3']"###,###.000</xsl:when>
</xsl:choose>
The traditional quick-and-dirty answer involves using the value to
control the length of a substring extraction from ".000000000000000",
and appending that to ###,###. (A bit more work is needed to suppress
the decimal point when the value is 0, but you get the idea.) The upper
limit is however many zeros you provide in the literal string, but
realistically your floating-point system also has accuracy limits so
asking for too high a precision's not useful anyway.

The completely general answer is a recursive named-template with
parameter (in lieu of counter loop), which appends a zero each time the
count is advanced.

--
() ASCII Ribbon Campaign | Joe Kesselman
/\ Stamp out HTML e-mail! | System architexture and kinetic poetry
Mar 9 '07 #2
On Mar 9, 2:02 pm, "Dag Sunde" <m...@dagsunde.comwrote:
I want to use the format-number function to output
a number with a specific number of decimals, based on the
content of a node... Is there any smart way to generate
differnet strings based on a node-value?

<xsl:choose>
<xsl:when test="decimals[.='0']"###,### </xsl:when>
<xsl:when test="decimals[.='1']"###,###.0</xsl:when>
<xsl:when test="decimals[.='2']"###,###.00</xsl:when>
<xsl:when test="decimals[.='3']"###,###.000</xsl:when>
<xsl:otherwise###,### </xsl:otherwise>
</xsl:choose>
Divide and conquer.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="valuedef">
<xsl:variable name="fmt">
<xsl:apply-templates select="decimals"/>
</xsl:variable>
<xsl:value-of select="format-number(value,$fmt)"/>
</xsl:template>
<xsl:template match="decimals">
<xsl:text>###,###.</xsl:text>
<xsl:call-template name="dec"/>
</xsl:template>
<xsl:template name="dec">
<xsl:param name="n" select="."/>
<xsl:choose>
<xsl:when test="$n &lt; 2">
<xsl:text>0</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="f" select="floor($n div 2)"/>
<xsl:call-template name="dec">
<xsl:with-param name="n" select="$f"/>
</xsl:call-template>
<xsl:call-template name="dec">
<xsl:with-param name="n" select="$n - $f"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

--
Pavel Lepin

Mar 9 '07 #3
Dag Sunde wrote:
I want to use the format-number function to output
a number with a specific number of decimals, based on the content
of a node... Is there any smart way to generate differnet strings
based on a node-value?

if decimals = 0, I want "###,###"
decimals = 1, I want "###,###.0"
decimals = 2, I want "###,###.00"
decimals = n, I want "###,###.000..n"
<snipped/>

Thank you both, Joe and Pavel!

Now I can get a little more flexibility...

--
Dag.

Mar 9 '07 #4
For the unconstrained solution in XSLT 2.0 one would simply use:

<xsl:value-of separator="" select=
"'###,###', if(decimals gt 0) then '.' else (), f:repeat('0',
decimals)"/>

where f:repeat is defined very simply in FXSL 2.0 as:

<xsl:function name="f:repeat" as="item()+">
<xsl:param name="pThis" as="item()"/>
<xsl:param name="pTimes" as="xs:integer"/>

<xsl:for-each select="1 to $pTimes">
<xsl:sequence select="$pThis"/>
</xsl:for-each>
</xsl:function>
For an efficient solution of the string-allocation problem in XSLT 1.0 see
something I published many years ago, which since then was used in the
implementation of the EXSLT str:padding() function:

http://sources.redhat.com/ml/xsl-lis.../msg01040.html

Hope this helped.
Cheers,
Dimitre Novatchev
"Dag Sunde" <me@dagsunde.comwrote in message
news:45***********************@news.wineasy.se...
>I want to use the format-number function to output
a number with a specific number of decimals, based on the content
of a node... Is there any smart way to generate differnet strings
based on a node-value?

if decimals = 0, I want "###,###"
decimals = 1, I want "###,###.0"
decimals = 2, I want "###,###.00"
decimals = n, I want "###,###.000..n"

Today I'm using <xsl:choose>, but that is limited to
hand-coding the alternatives.

I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Transform.xslt"?>
<root>
<valuedef>
<value>7</value>
<decimals>2</decimals>
</valuedef>
<valuedef>
<value>42</value>
<decimals>3</decimals>
</valuedef>
</root>

And I transform it like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
<xsl:apply-templates select="/root/valuedef" />
</xsl:template>

<xsl:template match="valuedef">
<value>
<xsl:variable name="tall" select="value"/>
<xsl:variable name="maske">
<xsl:choose>
<xsl:when test="decimals[.='0']"###,### </xsl:when>
<xsl:when test="decimals[.='1']"###,###.0</xsl:when>
<xsl:when test="decimals[.='2']"###,###.00</xsl:when>
<xsl:when test="decimals[.='3']"###,###.000</xsl:when>
<xsl:otherwise###,### </xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select='format-number($tall, $maske)' />
</value>
</xsl:template>
</xsl:stylesheet>

TIA...

--
Dag.


Mar 10 '07 #5
Dimitre Novatchev wrote:
For the unconstrained solution in XSLT 2.0 one would simply use:

<xsl:value-of separator="" select=
"'###,###', if(decimals gt 0) then '.' else (), f:repeat('0',
decimals)"/>

where f:repeat is defined very simply in FXSL 2.0 as:

<xsl:function name="f:repeat" as="item()+">
<xsl:param name="pThis" as="item()"/>
<xsl:param name="pTimes" as="xs:integer"/>

<xsl:for-each select="1 to $pTimes">
<xsl:sequence select="$pThis"/>
</xsl:for-each>
</xsl:function>
For an efficient solution of the string-allocation problem in XSLT
1.0 see something I published many years ago, which since then was
used in the implementation of the EXSLT str:padding() function:

http://sources.redhat.com/ml/xsl-lis.../msg01040.html

Hope this helped.
Now *That* was elegant!

Thanks...

--
Dag.
Mar 10 '07 #6
Dag Sunde wrote:
Dimitre Novatchev wrote:
>For the unconstrained solution in XSLT 2.0 one would simply use:

<xsl:value-of separator="" select=
"'###,###', if(decimals gt 0) then '.' else (), f:repeat('0',
decimals)"/>

where f:repeat is defined very simply in FXSL 2.0 as:

<xsl:function name="f:repeat" as="item()+">
<xsl:param name="pThis" as="item()"/>
<xsl:param name="pTimes" as="xs:integer"/>

<xsl:for-each select="1 to $pTimes">
<xsl:sequence select="$pThis"/>
</xsl:for-each>
</xsl:function>
For an efficient solution of the string-allocation problem in XSLT
1.0 see something I published many years ago, which since then was
used in the implementation of the EXSLT str:padding() function:

http://sources.redhat.com/ml/xsl-lis.../msg01040.html

Hope this helped.
Now *That* was elegant!
....Only to discover that there is now version of msxml that supports
XSLT 2.0...

Oh, well...

--
Dag.

Mar 10 '07 #7
>Now *That* was elegant!
>>

...Only to discover that there is now version of msxml that supports
XSLT 2.0...

Oh, well...
If one does have to work in an Microsoft XML/XSLT environment and wants to
use XSLT 2.0, then a good choice is Saxon.NET.
Cheers,
Dimitre Novatchev.

"Dag Sunde" <me@dagsunde.comwrote in message
news:45***********************@news.wineasy.se...
Dag Sunde wrote:
>Dimitre Novatchev wrote:
>>For the unconstrained solution in XSLT 2.0 one would simply use:

<xsl:value-of separator="" select=
"'###,###', if(decimals gt 0) then '.' else (), f:repeat('0',
decimals)"/>

where f:repeat is defined very simply in FXSL 2.0 as:

<xsl:function name="f:repeat" as="item()+">
<xsl:param name="pThis" as="item()"/>
<xsl:param name="pTimes" as="xs:integer"/>

<xsl:for-each select="1 to $pTimes">
<xsl:sequence select="$pThis"/>
</xsl:for-each>
</xsl:function>
For an efficient solution of the string-allocation problem in XSLT
1.0 see something I published many years ago, which since then was
used in the implementation of the EXSLT str:padding() function:

http://sources.redhat.com/ml/xsl-lis.../msg01040.html

Hope this helped.
Now *That* was elegant!

...Only to discover that there is now version of msxml that supports
XSLT 2.0...

Oh, well...

--
Dag.

Mar 10 '07 #8

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

reply views Thread by Sergio del Amo | last post: by
12 posts views Thread by Keith Chadwick | last post: by
4 posts views Thread by David S. Alexander | last post: by
3 posts views Thread by Ian Roddis | last post: by
3 posts views Thread by abhishek.smu | last post: by
3 posts views Thread by super.raddish | last post: by
11 posts views Thread by =?ISO-8859-1?Q?Jean=2DFran=E7ois_Michaud?= | last post: by
1 post views Thread by CARIGAR | last post: by
reply views Thread by zhoujie | last post: by
reply views Thread by suresh191 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.