tags:

views:

786

answers:

3

Hi All,

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:

  • 1,234.5
  • 1.234,5
  • 1234,5
  • 1234.5

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"&gt;
    <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

+3  A: 

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)"/>
David Lichteblau
A: 

The following transformation calculates which number-format to use:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <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
Dimitre Novatchev
the '0' format symbol may not follow the '#' format symbol in this section of a format pattern
Martijn
This means that numberformat3 as defined in the question conflicts with the picture used: '#,##0.000'. It is in your authority to ensure the number formats are correct. The problem as stated was how to organize a format to be passed dynamically, and this is shown in my answer
Dimitre Novatchev
Check, I got it working.Thanks a lot!
Martijn
A: 

Hi, Thanks for the feedback.

Dimitre, I tried your solution which works as defined, but when I put in a different format in the XML it does not work.

<?xml version="1.0" encoding="UTF-8"?>
<document>
  <settings>
    <DefaultNumberFormat>1.250,65</DefaultNumberFormat>
  </settings>

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

Any suggestions?

Martijn
This means that numberformat3 as defined by the OP conflicts with the picture used: '#,##0.000'. It is in his authority to ensure the number formats are correct. The problem as stated was how to organize a format to be passed dynamically, and this is shown in my answer
Dimitre Novatchev
This is not an answer.
Svante