我有以下xml文件:
<Contract>
<TotalCosts>31003</TotalCosts>
<PaymentSchedules>
<PaymentSchedule>
<Amount>516.7</Amount>
<NumberOfPayments>3</NumberOfPayments>
</PaymentSchedule>
<PaymentSchedule>
<Amount>529.7</Amount>
<NumberOfPayments>1</NumberOfPayments>
</PaymentSchedule>
<PaymentSchedule>
<Amount>516.7</Amount>
<NumberOfPayments>55</NumberOfPayments>
</PaymentSchedule>
<PaymentSchedule>
<Amount>504.70</Amount>
<NumberOfPayments>1</NumberOfPayments>
</PaymentSchedule>
</PaymentSchedules>
</Contract>
在我的schematron文件中,我想使TotalCosts与paymentSchedule中的金额相同。
为此,我需要为每个PaymentSchedule执行以下操作:
金额* NumberOfPayments
在此之后,我需要获取所有PaymentSchedules的总和,并且该数字应该完全相同。如果您针对给定示例尝试此操作,您将看到金额完全相同。
为了在schematron中验证这一点,我创建了这个schematron文件:
<iso:schema xmlns:iso="http://purl.oclc.org/dsdl/schematron"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" queryBinding="xslt2">
<iso:ns uri="http://www.someurl.com" prefix="func"/>
<xsl:function name="func:getPaymentScheduleTotal">
<xsl:param name="contract" as="element()"/>
<xsl:value-of
select="sum($contract/PaymentSchedules/PaymentSchedule/(NumberOfPayments * Amount))"/>
</xsl:function>
<iso:pattern id="Contract">
<iso:rule context="Contract">
<iso:assert test="func:getPaymentScheduleTotal(.) = number(TotalCosts)">amount in paymentschedule(<iso:value-of select="func:getPaymentScheduleTotal(.)"/>) doesn't match total amount(<iso:value-of select="TotalCosts"/>)</iso:assert>
</iso:rule>
</iso:pattern>
</iso:schema>
但这是我遇到问题的地方。我得到以下验证结果:
paymentchedule(31003.000000000004)金额与总金额(31003)不匹配(func:getPaymentScheduleTotal(。)= number(TotalCosts))[断言]
我无法弄清楚.000000000004的来源。我当然可以使用像地板这样的数字来绕数字,但我相信.000000000004首先不应该存在。
有什么想法吗?
答案 0 :(得分:3)
您遇到的问题是双变量如何工作的预期行为。 XSLT处理器将Amount和NumberOfPayments解释为xs:double数据类型。
您可能知道,xs:double用64位表示,遵循IEEE754标准(可以找到规范http://grouper.ieee.org/groups/754/)。主要问题是某些数字不能表示为xs:double,因此这些数字表示为可以表示的最接近的数字(有时它是算术运算的中间结果,是无法表示的结果)。 / p>
但是有一个解决方案可以解决您的问题。您可以使用xs:decimal而不是使用xs:double(使用指数来存储数字),根据数据类型规范,它不能包含指数,因此它必须按原样存储数字。所以,用
替换表达式的值 <xsl:value-of select="sum($contract/PaymentSchedules/PaymentSchedule/(xs:decimal(Amount) * xs:decimal(NumberOfPayments)))" />
应该按预期工作。
更新:完整的解决方案是:
<iso:schema xmlns:iso="http://purl.oclc.org/dsdl/schematron"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsl="http://www.w3.org/2001/XMLSchema" queryBinding="xslt2">
<iso:ns uri="http://www.someurl.com" prefix="func"/>
<xsl:function name="func:getPaymentScheduleTotal" as="xs:decimal">
<xsl:param name="contract" as="element()"/>
<xsl:sequence
select="sum($contract/PaymentSchedules/PaymentSchedule/(xs:decimal(NumberOfPayments) * xs:decimal(Amount)))"/>
</xsl:function>
<iso:pattern id="Contract">
<iso:rule context="Contract">
<iso:assert test="func:getPaymentScheduleTotal(.) = xs:decimal(TotalCosts)">amount in paymentschedule(<iso:value-of select="func:getPaymentScheduleTotal(.)"/>) doesn't match total amount(<iso:value-of select="TotalCosts"/>)</iso:assert>
</iso:rule>
</iso:pattern>
</iso:schema>
答案 1 :(得分:0)
听起来十进制表示(例如516.7,529.7 ......)是有限的,但相同值的二进制表示可能不是。因此,您会得到结果的差异。我会说除了舍入结果之外别无选择。
希望这有帮助。