0

我有以下 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>

但这是我遇到问题的地方。我得到以下验证结果:

Paymentschedule(31003.000000000004) 中的金额与总金额不匹配 (31003) (func:getPaymentScheduleTotal(.) = number(TotalCosts)) [assert]

我不知道 .000000000004 是从哪里来的。我当然可以使用像 floor 这样的东西来四舍五入,但我相信 .000000000004 一开始就不应该在那里。

有任何想法吗?

4

2 回答 2

3

您遇到的问题是双变量如何工作的预期行为。XSLT 处理器将 Amount 和 NumberOfPayments 解释为 xs:double 数据类型。

您可能知道,xs:double 用 64 位表示并遵循 IEEE754 标准(可以在http://grouper.ieee.org/groups/754/找到规范)。主要问题是某些数字不能表示为 xs:double,因此这些数字表示为可以表示的最接近的数字(有时它是算术运算的中间结果,无法表示)。

但是,您的问题有一个解决方案。除了使用 xs:double (使用指数来存储数字),您可以使用 xs:decimal ,根据数据类型规范不能包含指数,因此它必须按原样存储数字。因此,将 value-of 表达式替换为

    <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>
于 2013-02-19T15:33:42.180 回答
0

听起来十进制表示(例如 516.7、529.7 ...)是有限的,但相同值的二进制表示可能不是。因此,您会得到结果的差异。我会说除了四舍五入之外没有其他选择。

希望这可以帮助。

于 2013-02-19T15:25:45.083 回答