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

Discussion in 'XML' started by Dag Sunde, Mar 9, 2007.

  1. Dag Sunde

    Dag Sunde Guest

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

    --
    Dag.
    Dag Sunde, Mar 9, 2007
    #1
    1. Advertising

  2. Re: XSLT: Generating a format mask string based on the content ofa node?

    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
    Joe Kesselman, Mar 9, 2007
    #2
    1. Advertising

  3. Dag Sunde

    Guest

    On Mar 9, 2:02 pm, "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?
    >
    > <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>

    --
    Pavel Lepin
    , Mar 9, 2007
    #3
  4. Dag Sunde

    Dag Sunde Guest

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

    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.
    Dag Sunde, Mar 9, 2007
    #4
  5. Re: Generating a format mask string based on the content of a node?

    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


    "Dag Sunde" <> wrote in message
    news:45f14a98$0$22511$...
    >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...
    >
    > --
    > Dag.
    >
    >
    Dimitre Novatchev, Mar 10, 2007
    #5
  6. Dag Sunde

    Dag Sunde Guest

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

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

    Now *That* was elegant!

    Thanks...

    --
    Dag.
    Dag Sunde, Mar 10, 2007
    #6
  7. Dag Sunde

    Dag Sunde Guest

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

    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-list/2001-07/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.
    Dag Sunde, Mar 10, 2007
    #7
  8. Re: Generating a format mask string based on the content of a node?

    >> 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" <> wrote in message
    news:45f320a8$0$31539$...
    > 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-list/2001-07/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.
    >
    >
    >
    Dimitre Novatchev, Mar 10, 2007
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    1,375
  2. Tjerk Wolterink
    Replies:
    2
    Views:
    1,392
    Dimitre Novatchev
    Aug 24, 2006
  3. njsimha
    Replies:
    0
    Views:
    760
    njsimha
    Sep 16, 2008
  4. Marcin Tyman

    Conversion mask in hex to bit mask

    Marcin Tyman, May 6, 2008, in forum: Ruby
    Replies:
    4
    Views:
    762
    Robert Klemme
    May 6, 2008
  5. 187
    Replies:
    2
    Views:
    527
    Bart Lateur
    Jul 29, 2004
Loading...

Share This Page