XQuery 中不一致的空白处理?

Inconsistent Whitespace handling in XQuery?

我对如何让 XQuery 像我希望的那样处理 whitespace 感到困惑。 假设我必须关注 XML:

<body>
to<lb/>
<choice norm="Miss">Mi<glyph ref="#sm-long-s>s</glyph>s</choice>
<name type="person"><forename>Margaret</forename> <surname>Hamilton</surname></name><lb />
<name type="place">S<hi rend="superscript">t</hi> James's</name>
</body>

如果我使用这个代码

for $body in /body
return replace(string-join(
    for $t in $body//node()
    return
        typeswitch($t)
        case text() return
            if (
                sum(
                    for $a in $t/ancestor::*
                    return
                        typeswitch($a)
                        case element(choice) return 1
                        default return 0
                )=0
            ) then $t
            else null
        case element(lb) return ' '
        case element(choice) return $t/@norm
        default return null
),"\s+"," ")

我得到以下输出:

to MissMargaretHamilton St James's

而不是预期

to Miss Margaret Hamilton St James's

有办法解决这个问题吗?

PS:实际代码中没有<forename>这样的东西,但是我在这个例子中引入它来展示换行符和space ] 在 > 和 < 之间被忽略。

XML 空格处理可能会变得非常棘手。我经常需要尝试才能让事情恰到好处。

我喜欢编写转换函数,主要处理我 typeswitch 中的不同元素:

declare function local:transform($x)
{
  typeswitch($x)
  case element(choice) return $x/@norm/fn:string()
  case element(name) return
    if ($x/forename)
    then fn:string-join($x/node()/fn:string(), " ")
    else $x/fn:string()
  case element() return
    for $y in $x/node()
    return local:transform($y)
  default return fn:string($x)
};

let $x := (: your sample xml :)
return fn:replace(fn:string-join(local:transform($x), " "), "\s+", " ")

此示例应该 return 您想要的输出。并且很容易为其他元素添加案例,注释掉现有案例等

这个查询有一些非常奇怪的地方。例如,在我看来,这个子表达式:

            sum(
                for $a in $t/ancestor::*
                return
                    typeswitch($a)
                    case element(choice) return 1
                    default return 0
            )=0 

只是一种复杂的写法empty($t/ancestor::choice)

什么是"null"?在我看来,它就像一个元素名称,与您输入的任何内容都不匹配,因此是一种复杂的书写方式 ().

此外,您的 XML 格式不正确:ref 属性中缺少引号。这使我怀疑提交的问题不是最初执行的问题,因此您可能无意中删除了解决方案的线索。

但是,如果我在 Saxon 中修复丢失的引号和 运行 查询,它会产生预期的输出。所以我认为问题在于您的 XQuery 处理器中存在错误(或者更礼貌地说,不符合规范)。

稍后:经过反思,我怀疑您正在使用一个 XML 解析器来去除空白文本节点。这是 Microsoft MSXML 解析器的一个臭名昭著的怪癖,并且使得它在处理此类空格很重要的混合内容时非常无用。我相信它可以配置为表现 "properly",但我完全忘记了如何表现。

XQuery 规范确实在这方面给处理器留下了一定的自由度:它们允许以处理器喜欢的任何方式构建 XDM 输入树,这可能包括去除所有空格,或去除字母的每个出现 "x"。此时的问题是您是否发现您的特定 XQuery 处理器做出的设计选择可以接受。

为了更好地衡量,以下是我将如何重写您的查询:

normalize-space(string-join(
    for $t in /body//node()
    return
        typeswitch($t)
        case text() return $t[not(ancestor::choice)]
        case element(lb) return ' '
        case element(choice) return $t/@norm
        default return ()
))