Xml Schematron计算错误

Xml Schematron计算错误,xml,xslt,schematron,Xml,Xslt,Schematron,我有以下xml文件: <Contract> <TotalCosts>31003</TotalCosts> <PaymentSchedules> <PaymentSchedule> <Amount>516.7</Amount> <NumberOfPayments>3</NumberOfPayments>

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

31003
516.7
3.
529.7
1.
516.7
55
504.70
1.
在我的schematron文件中,我想确定总成本与paymentSchedule中的金额相同

为此,我需要为每个付款计划执行以下操作:

金额*付款数量

在那之后,我需要计算所有付款计划的总和,这个数字应该完全相同。如果您在给定的示例中尝试此操作,您将看到金额完全相同

为了在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()中的金额与总额()不匹配
但这就是我遇到问题的地方。我得到以下验证结果:

paymentschedule(31003.000000000004)中的金额与总金额(31003)不匹配(func:getPaymentScheduleTotal(.)=数字(TotalCosts))[断言]

我不知道.000000000004是从哪里来的。当然,我可以使用类似于floor的东西来对数字进行四舍五入,但我认为.000000000004不应该放在第一位


有什么想法吗?

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


希望这能有所帮助。

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


希望这有帮助。

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

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

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

    <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>

paymentschedule()中的金额与总额()不匹配

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

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

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

    <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>

paymentschedule()中的金额与总额()不匹配

这看起来像是将值视为浮点数而产生的舍入错误。如果您可以将数字视为
xs:decimal
,那么它应该使用任意精度的算术运算,而不是浮点数。这看起来像是将值视为浮点数而产生的舍入错误。如果您可以将数字视为
xs:decimal
,那么它应该使用任意精度的算术运算而不是浮点运算。您还需要声明
xs
前缀,并使用在断言中强制转换的相同类型(
func:getPaymentScheduleTotal(.)=xs:decimal(TotalCosts)
)我已经在我的答案中添加了完整的解决方案。为了完全准确,他还需要更改函数的结果类型,方法是在函数中添加as=“xs:decimal”,并使用xsl:sequence返回类型化元素,而不是返回文本节点。谢谢你的建议:)这对给定的示例有效。但是,对于需要任意小数位数的输入,IMHO的解决方案是不针对func:getPaymentScheduleTall(.)=xs:decimal(TotalCosts)这样的精确等式进行测试因此,四舍五入并根据区间进行测试是更好的解决方案。在这种情况下,金额是价格,因此他可能不会期望一个任意长的数字。此外,xs:decimal实现依赖于XSLT处理器(根据规范,它们必须支持至少18位十进制数字),因此一些处理器(如SAXON)支持无限精度()。我同意您的看法,如果您不考虑当前示例(我认为我的解决方案更适合OP发布的代码),并且代码不是针对特定XSLT处理器的,那么您的解决方案是