Reputation: 107
I need to test 3 attributes of a node. Problem is that I must return error for each attributes in error and I don't see how to achieve that in an easy way.
xquery is not really flexible so... not so much to try...
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>
)
So in this example I want to be able to trigger the 3 errors at one time, not one of them (like it is now). I don't even know if it is possible...
Thanks !
Upvotes: 0
Views: 84
Reputation: 163458
Start by putting the repeated code into a function:
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 ()
};
Then your logic reduces to
for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
return (local:check($theHoldingListsField/@AFL),
local:check($theHoldingListsField/@attitudeIndicator),
...)
Upvotes: 4
Reputation: 3517
Another option is to take a more functional programming approach.
We can generalise your test into a function that operates on a theHoldingListsField
as it has only two invariants, the attribute name ($attr-name
) and the error code ($error-id
).
We basically loop over the attributes (with error codes) that you want to test and call the local:test
function on each, e.g.
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, .)
The example above makes use of XQuery 3.1 features such as arrays ([]
), partial function application (?
), simple map operator (!
), and higher-order-functions (fn:apply
). I would suggest learning about those from the XQuery 3.1 W3C spec.
This could also be rewritten to remove the for
, and instead have the local:test
function operate on all fields (i.e. theHoldingListsFields
).
Upvotes: 2
Reputation: 4241
There is no if
without else
in standard XQuery, since if
/then
/else
is an expression which has to evaluate to some return value in every case (see functional programming).
If you want to return an empty sequence when the error condition is not met, you can do that explicitly, separately for each error. You can then gather all zero-or-one-element sequences into one, which is automatically flattened:
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>
) else ()
)
Upvotes: 2