开发者

locale-aware number comparison with XSLT

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.


I. XSLT 1.0 Solution:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <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>


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜