基于输入XML的XSLT十进制格式

基于输入XML的XSLT十进制格式,xml,xslt,formatting,decimal,xslt-1.0,Xml,Xslt,Formatting,Decimal,Xslt 1.0,我的XSLT文件应该有条件地显示价格和小数,这取决于输入XML是否包含小数。因此,我可以接收具有两种类型值的XML文件—XML将包含所有格式为小数最多两位的价格(我称之为“十进制XML”),或者价格将四舍五入到最接近的整数(我称之为“整数XML”) 我的问题是,我需要在XSLT文件中尽可能少地重构,同时允许它们以与XML输入相同的格式将转换应用于XHTML。为了实现这一目标,我向我的团队实施并建议了三条准则: 在计算值或将其存储在变量中时,删除所有format-number()函数调用。改用nu

我的XSLT文件应该有条件地显示价格和小数,这取决于输入XML是否包含小数。因此,我可以接收具有两种类型值的XML文件—XML将包含所有格式为小数最多两位的价格(我称之为“十进制XML”),或者价格将四舍五入到最接近的整数(我称之为“整数XML”)

我的问题是,我需要在XSLT文件中尽可能少地重构,同时允许它们以与XML输入相同的格式将转换应用于XHTML。为了实现这一目标,我向我的团队实施并建议了三条准则:

  • 在计算值或将其存储在变量中时,删除所有
    format-number()
    函数调用。改用
    number()
    。但是,某些条件适用于此规则(见下文)
  • 要显示该值时,请使用
    格式编号(,'#.###')
    格式。这将确保整数或十进制值将显示为XML中最初显示的值
  • 对于可选标记(如“折扣”),使用
    格式编号(,'0.00')
    函数,即使仅计算值。这是必要的,因为如果没有标记,尝试获取值将给出
    NaN
    结果
  • 下面是XSLT的一个示例:

      <x:stylesheet version="1.0" xmlns:x="http://www.w3.org/1999/XSL/Transform">
      <x:template match="/">
        <html>
          <body>
            <table border="1" width="60%">
              <tr>
                <th>Simple</th>
                <th>number()</th>
                <th>format-number(&lt;expression&gt;, '0.00')</th>
                <th>format-number(&lt;expression&gt;, '#.##')</th>
              </tr>
              <x:apply-templates />
            </table>
          </body>
        </html>
      </x:template>
    
      <x:template match="Item">
        <x:variable name="qty" select="number(@numItems)" />
        <x:variable name="cost" select="number(ItemCost) * $qty" />
        <x:variable name="extraCharges" select="(number(Tax) + number(TxnFee)) * $qty"/>
        <x:variable name="discount" select="format-number(Discount, '0.00') * $qty"/>
        <tr>
          <td>
          <!-- Works for Integer-XML, but values in Decimal-XML are
          *sometimes* rendered upto 14 decimal places. Even though Quickwatch
          shows it correctly in the debugger in VS. I cannot figure out what's
          special about the error cases. -->
            <x:value-of select="$cost + $extraCharges - $discount"/>
          </td>
          <td>
            <!-- Works same as the above case. -->
            <x:value-of select="number($cost + $extraCharges - $discount)"/>
          </td>
          <td>
          <!-- Works for Decimal-XML, but values in Integer-XML are always
          rendered with decimal digits. -->
            <x:value-of select="format-number(($cost + $extraCharges - $discount), '0.00')"/>
          </td>
          <td>
          <!-- Works for Integer-XML, but some values in Decimal-XML are
          rendered incorrectly. For example, 95.20 is rendered as 95.2;
          95.00 is rendered as 95 -->
            <x:value-of select="format-number(($cost + $extraCharges - $discount), '#.##')"/>
          </td>
        </tr>
      </x:template>
    </x:stylesheet>
    
    
    简单的
    编号()
    格式编号(表达式“0.00”)
    格式编号(表达式“#.##”)
    
    正如HTML注释所指出的,它在大多数情况下有效,但并非所有情况下都有效

    我希望使用一个表达式来格式化与输入相同的所有价格,而不是到处应用“whenother”构造,这还需要一个传递给XSLT的布尔参数来确定显示格式。XSLT的当前状态是,使用
    格式编号(,'0')
    对所有数字进行四舍五入,而不考虑输入

    我如何做到这一点


    编辑:在Dimitre的评论之后,我决定创建一个示例XML(以及上面的XSLT),以便专家们可以轻松地进行尝试

    示例XML(这个包含小数):

    
    10.99
    3.99
    2.99
    2.99
    15.50
    5.50
    3.50
    3.50
    
    如果我理解正确,您需要:

    • “整数XML”始终显示为四舍五入的数字,没有小数位数
    • “Decimal XML”总是以两位小数显示
    • 不是使用进行格式化的模板,而是XPath单行程序
    最接近您想要的是以下表达式:

    <xsl:value-of select="
      format-number($cost + $extraCharges - $discount, '0.00')
    " />
    
    但是,当
    $cost
    $extraCharges
    $discount
    加起来是一个整数时,此“失败”(删除小数)

    没有吸引力的替代方法是使用贝克尔的方法(以of命名):

    从技术上讲,这是一个单一的班轮。这两个
    sub-string()
    部分基于
    $condition
    相互排斥,因此
    concat()
    将只返回一个值或另一个值

    实际上(特别是对你的情况),这将是一个难以管理的混乱局面,缓慢且完全隐藏了意图:

    concat( 
      substring(
        format-number($cost + $extraCharges - $discount, '0.00')
        , 1
        , number(
          contains($cost, '.')
        ) * string-length(format-number($cost + $extraCharges - $discount, '0.00'))
      ),
      substring(
        format-number($cost + $extraCharges - $discount, '#.##')
        , 1
        , number(
          not(contains($cost, '.')) 
        ) * string-length(format-number($cost + $extraCharges - $discount, '#.##'))
      )
    )
    

    由于您声明所有输入值都包含小数,或者没有小数,因此上述表达式仅检查
    $cost
    是否包含小数点。我想,检查
    $extraCharges
    $discount
    也是正确的。

    如果我理解正确,您需要:

    • “整数XML”始终显示为四舍五入的数字,没有小数位数
    • “Decimal XML”总是以两位小数显示
    • 不是使用进行格式化的模板,而是XPath单行程序
    最接近您想要的是以下表达式:

    <xsl:value-of select="
      format-number($cost + $extraCharges - $discount, '0.00')
    " />
    
    但是,当
    $cost
    $extraCharges
    $discount
    加起来是一个整数时,此“失败”(删除小数)

    没有吸引力的替代方法是使用贝克尔的方法(以of命名):

    从技术上讲,这是一个单一的班轮。这两个
    sub-string()
    部分基于
    $condition
    相互排斥,因此
    concat()
    将只返回一个值或另一个值

    实际上(特别是对你的情况),这将是一个难以管理的混乱局面,缓慢且完全隐藏了意图:

    concat( 
      substring(
        format-number($cost + $extraCharges - $discount, '0.00')
        , 1
        , number(
          contains($cost, '.')
        ) * string-length(format-number($cost + $extraCharges - $discount, '0.00'))
      ),
      substring(
        format-number($cost + $extraCharges - $discount, '#.##')
        , 1
        , number(
          not(contains($cost, '.')) 
        ) * string-length(format-number($cost + $extraCharges - $discount, '#.##'))
      )
    )
    

    由于您声明所有输入值都包含小数,或者没有小数,因此上述表达式仅检查
    $cost
    是否包含小数点。我想,检查
    $extraCharges
    $discount
    也是正确的。

    您甚至忘了提供最简单的XML文档。这是没有帮助的。@Dimitre:我没有忘记提供它,但我必须承认,我没有意识到需要一个源XML,因为这实际上不是一个转换问题。这是一个关于十进制格式的一般性问题。不过,如果有帮助的话,我会尝试创建一个。您甚至忘了提供最简单的XML文档。这是没有帮助的。@Dimitre:我没有忘记提供它,但我必须承认,我没有意识到需要一个源XML,因为这实际上不是一个转换问题。这是一个关于十进制格式的一般性问题。不过,如果有帮助的话,我会尝试创建一个。
    concat( 
      substring(
        format-number($cost + $extraCharges - $discount, '0.00')
        , 1
        , number(
          contains($cost, '.')
        ) * string-length(format-number($cost + $extraCharges - $discount, '0.00'))
      ),
      substring(
        format-number($cost + $extraCharges - $discount, '#.##')
        , 1
        , number(
          not(contains($cost, '.')) 
        ) * string-length(format-number($cost + $extraCharges - $discount, '#.##'))
      )
    )