如果没有其他方法,是否可以有多个?

时间:2019-06-04 07:44:01

标签: xquery

我需要测试一个节点的3个属性。 问题是我必须为错误的每个属性返回错误,而且我看不出如何轻松实现。

xquery并不十分灵活,因此...无需太多尝试...

for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
    return
        if ($theHoldingListsField/@AFL != "MANDATORY") then
        (
            <error id="DPR-CKSEM-DEP-SMF142-2">
                <args>
                    <arg value="{$theHoldingListsField/ancestor::node()/@id}"/>
                    <arg value="AFL = {$theHoldingListsField/@AFL}"/>
                </args>
                <location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
            </error>
        )
        else if ($theHoldingListsField/@attitudeIndicator != "MANDATORY") then
        (
            <error id="DPR-CKSEM-DEP-SMF142-2">
                <args>
                    <arg value="{$theHoldingListsField/ancestor::node()/@id}"/>
                    <arg value="attitudeIndicator = {$theHoldingListsField/@attitudeIndicator}"/>
                </args>
                <location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
            </error>
        )

因此,在此示例中,我希望能够一次触发这3个错误,而不是其中之一(就像现在一样)。 我什至不知道是否有可能...

谢谢!

3 个答案:

答案 0 :(得分:4)

首先将重复的代码放入函数中

declare function local:check($att as attribute()) as element(error)? {
  if ($att != "MANDATORY") then (
    <error id="DPR-CKSEM-DEP-SMF142-2">
      <args>
        <arg value="{$att/../ancestor::node()/@id}"/>
        <arg value="{name($att)} = {$att}"/>
      </args>
      <location value="{functx:path-to-node-with-pos($att/..)}"/>
    </error>
  ) else ()
};

然后您的逻辑简化为

for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
    return (local:check($theHoldingListsField/@AFL),
            local:check($theHoldingListsField/@attitudeIndicator),
            ...)

答案 1 :(得分:2)

在标准XQuery中,没有if的{​​{1}}不存在,因为else / if / then expression 在每种情况下都必须计算出某个返回值(请参见functional programming)。

如果要在不满足错误条件时返回空序列,则可以针对每个错误分别进行显式操作。然后,您可以将所有零或一元素序列收集为一个,然后将其自动展平:

else

答案 2 :(得分:2)

另一种选择是采用更具功能性的编程方法。

我们可以将您的测试概括为一个在theHoldingListsField上运行的函数,因为它只有两个不变式,即属性名称($attr-name)和错误代码($error-id)。 / p>

我们基本上遍历了要测试的属性(带有错误代码),并在每个属性上调用local:test函数,例如

declare function local:test($theHoldingListsField, $attr-name, $error-id) {
    $theHoldingListsField/@*[local-name() eq $attr-name][. ne "MANDATORY"] !
       <error id="{$error-id}">
         <args>
          <arg value="{$theHoldingListsField/ancestor::node()/@id}"/>
          <arg value="{$attr-name} = {.}"/>
         </args>
         <location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
       </error>
};


let $tests := (
  ["AFL", "DPR-CKSEM-DEP-SMF142-2"],
  ["attitudeIndicator", "DPR-CKSEM-DEP-SMF142-2"]
)

for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
let $test-fn := local:test($theHoldingListsField, ?, ?)
return
  $tests ! fn:apply($test-fn, .)

上面的示例利用了XQuery 3.1功能,例如数组([],部分函数应用程序(?),简单映射运算符(!)和高阶运算符。功能(fn:apply)。我建议从XQuery 3.1 W3C规范中了解这些内容。

也可以将其重写以删除for,而可以在所有字段(即local:test)上使用theHoldingListsFields函数。