XQuery FLWOR return 包含多个 if 语句的部分代码内容

XQuery FLWOR return section code content with multiple if statements

我正在尝试清理一些 XQuery 代码并将 运行 转化为另一个问题(最后一个问题是这个问题:)。我正在进一步清理以下工作代码,以尽量减少重用:

    for $noteLine in $noteLineArr
    where $noteLine != ''

    return (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
        </NTE>, 
        if (fn:string-length(fn:normalize-space($noteLine)) > 80 and fn:string-length(fn:normalize-space($noteLine)) <= 160) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
        </NTE>
        ) else if (fn:string-length(fn:normalize-space($noteLine)) > 160) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
        </NTE>,
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
        </NTE>
        ) else ()
    )   

既然我移出了第一个 NTE,我想基本上对其余的 NTE 做同样的事情(运行 一个 IF 并且只使用完全需要的代码输出另一个)所以我想象了这样一个解决方案:

    for $noteLine in $noteLineArr
    where $noteLine != ''

    return (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
        </NTE>, 
        if (fn:string-length(fn:normalize-space($noteLine)) > 80) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
        </NTE>
        ) else()
        if (fn:string-length(fn:normalize-space($noteLine)) > 160) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
        </NTE>
        ) else ()
    )   

这在解析器中失败,错误 "Unexpected Token: ' (fn:string-len...'" 指向第二个 if 语句。我得出的结论是 XQuery 不喜欢重复出现 if 部分,除非它们在一个节点内,如下所示:

    for $noteLine in $noteLineArr
    where $noteLine != ''

    return (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
        </NTE>, 
        <NTE>
        if (fn:string-length(fn:normalize-space($noteLine)) > 80) then (
            <NoteRefCd>BOL</NoteRefCd>,
            <Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
        ) else()
        </NTE>,
        <NTE>
        if (fn:string-length(fn:normalize-space($noteLine)) > 160) then (
            <NoteRefCd>BOL</NoteRefCd>,
            <Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
        ) else ()
        </NTE>
    )   

这是行不通的,因为如果这些额外的节点是空的,我不能让它们进入输出。 XQuery 中是否有一种方法可以以类似的方式实现所需的输出(我通过将一些代码放入本地函数来制定解决方案,但它不像 imo 那样可读)?可能作为后续,为什么 XQuery 对多个连续的 if 语句如此敏感?

解决方案(再次感谢 Jens Erat)

    for $noteLine in $noteLineArr
    where $noteLine != ''

    return (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
        </NTE>, 
        if (fn:string-length(fn:normalize-space($noteLine)) > 80) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
        </NTE>
        ) else(),
        if (fn:string-length(fn:normalize-space($noteLine)) > 160) then (
        <NTE>
            <NoteRefCd>BOL</NoteRefCd>
            <Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
        </NTE>
        ) else()
    )   

(注意:逗号放在return的前两个"sections"之后,第一个NTE节点之后,然后在if语句之后,不在if语句之内)。

XQuery 是一种函数式编程语言。除其他事项外,这意味着所有内容都有一个表达式,该表达式具有单个不同的 return 值。你也可以有 多个 语句 returning 一些东西,但是你总是必须将它们包装在一个序列中,这是一个将任意数量的输入表达式包装成一个 return值(序列)。

解释表达式

为了用更少的样板代码来解释这个问题,考虑一个像

这样的小例子
for $i in 1 to 10
return $i

这显然 return 是从一到十的所有数字。如果我们只想 return 偶数,使用 if 语句怎么办? if 语句的格式为 if (expression) then expression else expression。请记住:每个表达式都必须有一个 return 值,这也适用于 else 子句。在这种情况下,我们只是 return 空序列 (),在 XQuery 中表示 "no value".

for $i in 1 to 10
return
  if ($i mod 2 eq 0)
  then $i
  else ()

有多个 if 表达式

这很好用,return所有偶数。现在,假设我们 想要在数字可以被三除时打印一个字符串。我们不能简单地将其放入else子句中,例如6甚至都可以被三整除。我们需要分开 if 个子句。这也是您在第二个代码示例中尝试的方法。

for $i in 1 to 10
return
  if ($i mod 2 eq 0)
  then $i
  else (),
  if ($i mod 3 eq 0)
  then "three"
  else ()

这失败了,因为 $i 没有在第二个 if 子句中定义。但为什么?请记住:每个表达式都应该有一个 return 值。还有 return 语句的表达式,即 if then else 表达式。与您的第一个问题非常相似,XQuery 编译器将此示例视为

for $i in 1 to 10
return
  if ($i mod 2 eq 0)
  then $i
  else (),
if ($i mod 3 eq 0)
then "three"
else ()

这应该会使问题更加明显。最后,它与您的原始问题中的问题相同,但表达式更复杂。两者都是

形式
for $i in 1 to 10
return
  expression1,
  expression2

如果它们都属于 return 语句,则必须包含在一个序列中:

for $i in 1 to 10
return (
  expression1,
  expression2
)

你的旧问题中的表达式是 elements/element 构造函数,这里是 if then else 表达式。

多个if表达式的解决方案

同样,您必须按顺序包装它们:

for $i in 1 to 10
return (
  if ($i mod 2 eq 0)
  then $i
  else (),
  if ($i mod 3 eq 0)
  then "three"
  else ()
)

现在,序列是 return 语句的单个表达式,并且还有两个包含 if then else 代码的子表达式。当您混合不同类型的表达式时,这也是必需的,如本例所示:

for $i in 1 to 10
return (
  if ($i mod 2 eq 0)
  then $i
  else (),
  <foo/>
)

删除序列括号将使 XQuery 引擎输出偶数,然后输出一个 <foo/> 元素,而不是十个 <foo/> 元素——每个 $i 一个。