I have a very strange problem with a FLWOR loop, that works one way but not another. The goal is to take a string of any length, and break it into XML nodes that can only hold 80 chars each. So, first whack, this works great:
for $noteLine in $noteLineArr
where $noteLine != ''
return
if (fn:string-length(fn:normalize-space($noteLine)) < 80) then (
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
</NTE>
) else if (fn:string-length(fn:normalize-space($noteLine)) > 80 and fn:string-length(fn:normalize-space($noteLine)) <= 160) then (
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
</NTE>,
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>
) else if (fn:string-length(fn:normalize-space($noteLine)) > 160 and fn:string-length(fn:normalize-space($noteLine)) <= 240) then (
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 1, 80)}</Descr>
</NTE>,
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>,
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
</NTE>
) else()
So, I get it in my head that this isn't very elegant. I try to clean it up by moving the first element out of the if, since it should always get used, right? Less code that way? So here is what I tried:
for $noteLine in $noteLineArr
where $noteLine != ''
return
<NTE>
<NoteRefCd>WHI</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>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>
) else if (fn:string-length(fn:normalize-space($noteLine)) > 160 and fn:string-length(fn:normalize-space($noteLine)) <= 240) then (
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>,
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
</NTE>
) else()
And the parser is now telling me "undefined variable at noteLine" pointing to the first "if" line. What am I missing here? (note that yes, I do have other ideas of how to clean this up even more, but this was the first simple step and when it failed miserably, I panicked).
Solution (thanks to Jens Erat) The return block needs to be wrapped in parentheses in order to force it to evaluate fully on each iteration of the flwor loop. Thusly:
for $noteLine in $noteLineArr
where $noteLine != ''
return
(
<NTE>
<NoteRefCd>WHI</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>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>
) else if (fn:string-length(fn:normalize-space($noteLine)) > 160 and fn:string-length(fn:normalize-space($noteLine)) <= 240) then (
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 81, 80)}</Descr>
</NTE>,
<NTE>
<NoteRefCd>WHI</NoteRefCd>
<Descr>{fn:substring(fn:normalize-space($noteLine), 161, 80)}</Descr>
</NTE>
) else()
)
This is a minor syntax issue. You have to put parenthesis around the elements created in the
return
clause. To further explain the issue, I'm providing a similar, but simplified example:You'd expect to get following result (the two elements repeated three times):
But instead, you get
This is because the query is actually evaluated as
In your code, you don't get unexpected output, but an error message. This is because
$noteLine
is not defined after the first comma,
. You would get a similar error message for following query:Here,
$i
is not bound for the<bar/>
element, as the query is evaluated as