Martijn
Martijn

Reputation: 41

XSL:decimal-format with multiple possible formats

I have an interesting problem with XSL. We have an XML Input file that contains next to the content several settings that define how we output the content. Currently I'm struggling with the formatting of the numbers within the XSL file.

The following formats are possible:

I get these formats as above mentioned within my XML file:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <settings>
        <DefaultCurrency>EUR</DefaultCurrency>
        <DefaultDateFormat>DD.MM.YYYY</DefaultDateFormat>
    <DefaultNumberFormat>1.250,65</DefaultNumberFormat>
    <AllowOrdering>true</AllowOrdering>
</settings>
<productlist>
    <item>
        <product>
            <productid>Product1</productid>
            <weight>0.123</weight>
        </product>
        <customerprice>123.03</customerprice>
    </item>
    <item>
        <product>
            <productid>Product2</productid>
            <weight>12312.123</weight>
        </product>
        <customerprice>12.00</customerprice>
    </item>
    <item>
        <product>
            <productid>Product3</productid>
            <weight>12.123</weight>
        </product>
        <customerprice>13.23</customerprice>
    </item>
</productlist>
</document>

I have tried to get this working with the following XSL file, but I cannot get it to give me the correct results (when the DefaultNumberFormat in the XML is changed the formatting must also be changed).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:decimal-format name="numberformat1" decimal-separator="." grouping-separator=","/>
    <xsl:decimal-format name="numberformat2" decimal-separator=","/>
    <xsl:decimal-format name="numberformat3" decimal-separator="," grouping-separator="."/>
    <xsl:decimal-format name="numberformat4" decimal-separator="."/>
    <xsl:template match="productlist">
        <xsl:apply-templates select="item"/>
    </xsl:template>
    <xsl:template match="item">
ItemID: <xsl:value-of select="product/productid"/>
Weigh: 
<xsl:choose>
            <xsl:when test="/document/settings/DefaultNumberFormat = '1,250.65'">
                <xsl:value-of select="format-number(product/weight, '#,##0.000', 'numberformat1')"/>
            </xsl:when>
            <xsl:when test="/document/settings/DefaultNumberFormat = '1250,65'">
                <xsl:value-of select="format-number(product/weight, '#,##0.000', 'numberformat2')"/>
            </xsl:when>
            <xsl:when test="/document/settings/DefaultNumberFormat = '1.250,65'">
                <xsl:value-of select="format-number(product/weight, '#,##0.000', 'numberformat3')"/>
            </xsl:when>
            <xsl:when test="/document/settings/DefaultNumberFormat = '1250.65'">
                <xsl:value-of select="format-number(product/weight, '#,##0.000', 'numberformat4')"/>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="settings/*"/>
</xsl:stylesheet>

I hope somebody can help me here, I think I somehow got a mind twist in how to combine the format-number and the number format...

Thanks, Martijn

Upvotes: 1

Views: 4547

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243579

The following transformation calculates which number-format to use:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vDoc" select="/"/>

    <xsl:variable name="vDFormats">
        <dFormat spec="1,250.65" name="numberformat1"/>
        <dFormat spec="1250,65" name="numberformat2"/>
        <dFormat spec="1.250,65" name="numberformat3"/>
        <dFormat spec="1250.65" name="numberformat4"/>
    </xsl:variable>

    <xsl:variable name="vDefFormats" select=
     "document('')/*/xsl:variable
                      [@name='vDFormats']"/>

    <xsl:variable name="vtheFormatName" select=
     "string($vDefFormats/*
              [@spec = $vDoc/*/settings/DefaultNumberFormat]
                                   /@name
           )
   "/>

    <xsl:decimal-format name="numberformat1" 
         decimal-separator="." grouping-separator=","/>
    <xsl:decimal-format name="numberformat2" 
         decimal-separator=","/>
    <xsl:decimal-format name="numberformat3" 
         decimal-separator="," grouping-separator="."/>
    <xsl:decimal-format name="numberformat4" 
         decimal-separator="."/>

    <xsl:template match="productlist">
        <xsl:apply-templates select="item"/>
    </xsl:template>

    <xsl:template match="item">ItemID: <xsl:text/> 
        <xsl:value-of select="product/productid"/> Weigh: <xsl:text/> 
        <xsl:value-of select=
        "format-number(product/weight, 
                       '#,##0.000', 
                       $vtheFormatName)"/>
    </xsl:template>

    <xsl:template match="settings/*"/>
</xsl:stylesheet>

When applied on the following XML document:

<document>
  <settings>
    <DefaultNumberFormat>1,250.65</DefaultNumberFormat>
  </settings>

    <productlist>
      <item>
        <product>
          <productid>30</productid>
          <weight>2530.45</weight>
        </product>
      </item>
    </productlist>
</document>

the wanted result is produced:

ItemID: 30 Weigh: 2,530.450

Upvotes: 0

David Lichteblau
David Lichteblau

Reputation: 3561

In XSLT, format-number is an ordinary XPath function that takes strings as its second and third argument. These strings need not be specified as constants in the stylesheet -- they can also be computed at run-time from the source document.

In the easiest case, let's assume that the source document actually specifies the arguments directly as settings/picture an `settings/formatname'. If so, you can write:

<xsl:value-of select="format-number(product/weight,
                                    settings/picture,
                                    settings/formatname)"/>

Alternatively, if the document uses a different notation, making a translation step necessary, you can refactor your stylesheet to use XSLT variables like this:

<xsl:variable name="picture">
  <!-- compute the picture string here, e.g. using <xsl:choose> and -->
  <!-- /document/settings/DefaultNumberFormat -->
</xsl:variable>

<xsl:variable name="formatname">
  <!-- compute the number format name here, e.g. using <xsl:choose> and -->
  <!-- /document/settings/DefaultNumberFormat -->
</xsl:variable>

<xsl:value-of select="format-number(product/weight, $picture, $formatname)"/>

Upvotes: 3

Related Questions