Cer*_*rus 0 xml xslt formatting decimal xslt-1.0
我有一种情况,我的XSLT文件应有条件地显示价格和小数,具体取决于输入XML是否包含小数.所以,我可以接收带有两种类型值的XML文件 - XML将包含格式化为小数的所有价格,最多两个位置(我称之为"Decimal-XML")或者价格将四舍五入到最接近的整数(我称之为一个"整数XML").
我的问题是我需要在XSLT文件中尽可能少地重构,然后允许它们以与XML输入相同的格式将转换应用于XHTML.为了实现这一目标,我向我的团队实施并提出了三条指导原则:
format-number()在计算计算值或存储在变量中时,删除所有函数调用.请number(<value>)改用.但是,某些条件适用于此规则(见下文).format-number(<value>, '#.##')格式.这应该确保整数或十进制值将显示为最初存在于XML中.format-number(<value>, '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(<expression>, '0.00')</th>
<th>format-number(<expression>, '#.##')</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>
Run Code Online (Sandbox Code Playgroud)
正如HTML评论所述,它适用于大多数情况但不是全部.
我想使用单个表达式来格式化与输入相同的所有价格,而不是在任何地方应用"when-otherwise"构造,这将另外需要传递给XSLT的布尔参数来确定显示格式.XSLT的当前状态是所有数字都被舍入而不管输入如何使用format-number(<expression>, '0').
我该如何做到这一点?
编辑:在Dimitre的评论之后,我决定创建一个示例XML(以及上面的XSLT),以便专家可以轻松地尝试.
示例XML(这个包含小数):
<ShoppingList>
<Item numItems="2">
<ItemCost>10.99</ItemCost>
<Tax>3.99</Tax>
<TxnFee>2.99</TxnFee>
<Discount>2.99</Discount>
</Item>
<Item numItems="4">
<ItemCost>15.50</ItemCost>
<Tax>5.50</Tax>
<TxnFee>3.50</TxnFee>
<Discount>3.50</Discount>
</Item>
</ShoppingList>
Run Code Online (Sandbox Code Playgroud)
如果我理解正确,你想:
你得到的最接近的是这个表达式:
<xsl:value-of select="
format-number($cost + $extraCharges - $discount, '0.00')
" />
Run Code Online (Sandbox Code Playgroud)
由于在XPath 1.0(XPath 2.0if/then/else)中没有易于使用的条件表达式,因此妥协如下:
<xsl:value-of select="
substring-before(
concat(format-number($cost + $extraCharges - $discount, '0.00'), '.00')
, '.00'
)
" />
Run Code Online (Sandbox Code Playgroud)
但是,这种"失败"(切断小数)时$cost,$extraCharges以及$discount加起来的轮数.
不具吸引力的替代方案是使用贝克尔的方法(后命名为奥利弗·贝克尔的胡柏林):
concat(
substring($s1, 1, number($condition) * string-length($s1)),
substring($s2, 1, number(not($condition)) * string-length($s2))
)
Run Code Online (Sandbox Code Playgroud)
从技术上讲,这是一个单行.这两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, '#.##'))
)
)
Run Code Online (Sandbox Code Playgroud)
由于您声明所有输入值都包含小数,或者没有包含小数,因此上述表达式仅检查是否$cost包含小数点.这将是正确的检查$extraCharges和$discount以及,我猜.
| 归档时间: |
|
| 查看次数: |
15363 次 |
| 最近记录: |