XSD断言:按顺序将所有值相乘

时间:2018-12-07 23:31:13

标签: xml xpath xsd assert

我有一个xml文档,如下所示:

<Output>
    <Integer start="0 0 0 0 0 0 0"/>
    <Dimensions>
        <Dimension value="2"/>
        <Dimension value="3"/>
    </Dimensions>
</Output>

我还有一个XSD文件,它定义了相应的结构。 start属性定义为整数列表。 在此XSD文件中,我希望有一个断言,该断言检查此列表中的值的数量是否等于Dimension元素的所有值属性的乘积。在这种情况下,必须在开始时定义2 * 3 = 6个值。

类似于

<xs:assert test="count(data(./Integer/@start)) = multiply(./Dimensions/Dimension/@value) "/>

我的问题是如何将乘法写成类似于sum,但是用乘法而不是加法。是否可以向XSD文档添加功能?还是有可能重写断言测试以满足我的要求?

2 个答案:

答案 0 :(得分:1)

XSD 1.1断言仅限于XPath 2.0,这使这一工作变得困难。无法访问XPath 3.1 fold-left()函数或用户定义的函数(这将允许递归)。

某些XSD 1.1处理器可能会解除此限制(您可以将Saxon配置为允许XPath 3.1),但这超出了规范。

如果您准备限制“尺寸”的数量,则可以这样做

number(if (exists(Dimension[1])) then Dimension[1]/@value else 1) *
number(if (exists(Dimension[2])) then Dimension[2]/@value else 1) *
number(if (exists(Dimension[3])) then Dimension[3]/@value else 1) *
... etc

但是我认为没有使用纯XSD 1.1 + XPath 2.0的通用解决方案。

启用XPath 3.1后,您可以

count(...) = fold-left(Dimension/@value, 1, 
               function($total, $next){$total*number($next)})

答案 1 :(得分:0)

如果我对您的理解正确,那么您正在请求可用于列表/序列的乘法功能。

由于没有这样的内置函数afaik,因此需要这样的用户定义的递归函数(在XQuery的功能编程领域):

declare function local:rec-multiply($seq, $count) {
  if(empty($seq)) then ()
  else
    let $prod  := $seq[1], 
        $count := $count * $prod
    return (
      <count>{ $count }</count>,
      local:rec-multiply($seq[position() > 1], $count)
    )
};

count(tokenize(//Integer/@start, '\s')) 
  = local:rec-multiply(//Dimension/@value, 1)[last()]

Online Demo

注意事项:此代码需要XQuery 3+,并且常见的XSD处理器仅支持XPath 2.0(实际上通常仅支持子集)。如果您解除了这种限制,那么也可以使用XQuery的fold-left / right函数,如Michael Kay所述。

但是,最后,最简单的方法是提取这些值并以通用编程语言执行乘法和验证。