CodeKiller
CodeKiller

Reputation: 107

Is there a way to have multiple if without else?

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

Answers (3)

Michael Kay
Michael Kay

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

adamretter
adamretter

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

Leo W&#246;rteler
Leo W&#246;rteler

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

Related Questions