2

我正在尝试清理一些 XQuery 代码并遇到另一个问题(最后一个问题是这个问题:Error "undefined variable at noteLine" in FLWOR expression when return multiple nodes)。我正在进一步清理以下工作代码,以尽量减少重用:

    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 移出,我想对其余的基本上做同样的事情(运行一个 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()
    )   

(注意:逗号放置在返回的前两个“部分”之后,第一个 NTE 节点和 if 语句之后,而不是在 if 语句中)。

4

1 回答 1

3

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

解释表达式

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

for $i in 1 to 10
return $i

这显然会返回从一到十的所有数字。如果我们只想使用 if 语句返回偶数怎么办?该if语句的格式为if (expression) then expression else expression。请记住:每个表达式都必须有一个返回值,这也适用于else子句。在这种情况下,我们只是让它返回空序列(),这在 XQuery 中意味着“无值”。

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

有多个if表达式

这工作正常并返回所有偶数值。现在,让我们考虑一下,只要一个数字可以被三除,我们也想打印一个字符串。我们不能简单地将其放入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语句的表达式,也就是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
)

表达式是旧问题中的元素/元素构造函数,而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

于 2016-01-26T17:11:52.867 回答