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

D

Dag Sunde

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:eek:utput 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:eek:therwise> ###,### </xsl:eek:therwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select='format-number($tall, $maske)' />
</value>
</xsl:template>
</xsl:stylesheet>

TIA...
 
J

Joe Kesselman

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.
 
P

p.lepin

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:eek:therwise> ###,### </xsl:eek:therwise>
</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:eek:therwise>
<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:eek:therwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
 
D

Dag Sunde

Dag said:
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...
 
D

Dimitre Novatchev

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-list/2001-07/msg01040.html

Hope this helped.


Cheers,
Dimitre Novatchev
 
D

Dag Sunde

Dimitre said:
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-list/2001-07/msg01040.html

Hope this helped.
Now *That* was elegant!

Thanks...
 
D

Dimitre Novatchev

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top