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 ()
))
我对如何让 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 ()
))