views:

27

answers:

2

I have a bunch of costs in my XML that I am trying to compare against another cost value, to determine which values to display (only ones higher than the comparator). This works when the numbers use the decimal point as the separator but not with numbers using comma as the decimal separator. I could be getting either, depending on the locale.

Here's what I have:

<patron_max_cost><![CDATA[1,00]]></patron_max_cost>
<service_costs>
  <location_cost>
    <location_desc><![CDATA[location1]]></location_desc>
    <cost_to_user><![CDATA[0,99]]></cost_to_user>
  </location_cost>
  <location_cost>
      <location_desc><![CDATA[location2]]></location_desc>
      <cost_to_user><![CDATA[1,50]]></cost_to_user>
  </location_cost>
</service_costs>

<xsl:variable name="filtered_location_costs">
  <xsl:for-each select="service_costs/location_cost">
      <xsl:if test="number(cost_to_user) &gt; number(patron_max_cost)">
        <xsl:copy-of select="." />
      </xsl:if>
  </xsl:for-each>
</xsl:variable>

This fails because cost_to_user and patron_max_cost are NaN. Is there a way of doing this comparison that will work for both input styles and doesn't involve something kludgy like converting the commas to decimal points before comparing? I am using XSLT2.0 and Saxon8.

A: 

I would use the conversion from comma to decimal, but also keep a variable of whether there was a comma in the first place so that I could display it correctly (ie, convert it back.

Woody
A: 

I. XSLT 1.0 Solution:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "location_cost[not(translate(cost_to_user, ',', '.')
                     >
                     translate(/*/patron_max_cost, ',', '.')
                     )
                 ]
  "/>

</xsl:stylesheet>

when applied on the provided XML document:

<t>
    <patron_max_cost><![CDATA[1,00]]></patron_max_cost>
    <service_costs>
        <location_cost>
            <location_desc><![CDATA[location1]]></location_desc>
            <cost_to_user><![CDATA[0,99]]></cost_to_user>
        </location_cost>
        <location_cost>
            <location_desc><![CDATA[location2]]></location_desc>
            <cost_to_user><![CDATA[1,50]]></cost_to_user>
        </location_cost>
    </service_costs>
</t>

produces the wanted result:

<t>
   <patron_max_cost>1,00</patron_max_cost>
   <service_costs>
      <location_cost>
         <location_desc>location2</location_desc>
         <cost_to_user>1,50</cost_to_user>
      </location_cost>
   </service_costs>
</t>

II. XSLT 2.0 solution:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vpatron_max_cost" as="xs:decimal"
  select="xs:decimal(translate(/*/patron_max_cost, ',', '.'))"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "location_cost[xs:decimal(translate(cost_to_user, ',', '.'))
                le
                 $vpatron_max_cost
                 ]
  "/>

</xsl:stylesheet>
Dimitre Novatchev
Thanks Dimitre, that is simpler than I imagined it would be. I appreciate the extended answer as well.
Andy