XQuery FLWOR返回带有多个if语句的段代码内容

时间:2016-01-26 15:35:23

标签: xml if-statement xquery flwor

我正在尝试清理一些XQuery代码并遇到另一个问题(最后一个问题就是这个问题:Error "undefined variable at noteLine" in FLWOR expression when returning 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不喜欢重复,除非它们在节点,像这样:

    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语句之内)。

1 个答案:

答案 0 :(得分:2)

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

解释表达式

用较少的样板代码解释问题,请考虑一个小例子,如

for $i in 1 to 10
return $i

这显然会将所有数字从1返回到10。如果我们只想使用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表达式

这很好,并返回所有偶数值。现在,让我们考虑我们想要在数字可以除以3时打印字符串。我们不能简单地将其放入else子句中,例如6甚至都可以除以3。我们需要分开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/>元素,而不是10个<foo/>元素 - 每个$i一个。