Schematron计算错误

时间:2013-02-19 13:54:14

标签: xml xslt schematron

我有以下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首先不应该存在。

有什么想法吗?

2 个答案:

答案 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 ......)是有限的,但相同值的二进制表示可能不是。因此,您会得到结果的差异。我会说除了舍入结果之外别无选择。

希望这有帮助。