Xml Schematron计算错误
我有以下xml文件:Xml Schematron计算错误,xml,xslt,schematron,Xml,Xslt,Schematron,我有以下xml文件: <Contract> <TotalCosts>31003</TotalCosts> <PaymentSchedules> <PaymentSchedule> <Amount>516.7</Amount> <NumberOfPayments>3</NumberOfPayments>
<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处理器的,那么您的解决方案是