我很困惑如何让XQuery像我想要的那样处理空白。 假设我必须遵循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>
这样的东西,但我在这个例子中介绍了它以展示linebreak和&gt;之间的空格。和&lt;被忽略。
答案 0 :(得分:2)
这个查询有一些非常奇怪的事情。例如,在我看来这个子表达式:
sum(
for $a in $t/ancestor::*
return
typeswitch($a)
case element(choice) return 1
default return 0
)=0
只是一种令人费解的写作方式empty($t/ancestor::choice)
。
什么是&#34; null&#34;?在我看来,它就像一个元素名称,它不会与输入中的任何内容相匹配,因此是一种令人费解的写作方式()
。
更重要的是,您的XML格式不正确:ref属性上缺少引号。这让我怀疑提交的问题不是最初执行的问题,因此您可能无意中删除了解决方案的线索。
但是,如果我修复缺失的引用并在Saxon中运行查询,它会产生预期的输出。所以我认为问题在于你的XQuery处理器中存在一个错误(或者更有礼貌,不符合)。
LATER:经过反思,我怀疑你正在使用一个剥离空白文本节点的XML解析器。这是Microsoft MSXML解析器的一个臭名昭着的怪癖,并且在处理这种空白很重要的混合内容时非常无用。我相信它可以被配置为正确行事#34;但我已经完全忘记了如何。
XQuery规范确实让处理器在这方面有一定的自由度:它们允许以处理器所想的任何方式构造XDM输入树,这可能包括剥离所有空格,或者剥离字母的每一次出现&#34; X&#34 ;.在这一点上,您是否发现特定XQuery处理器所做的设计选择是可以接受的。
答案 1 :(得分:1)
为了更好地衡量,我将重写您的查询:
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 ()
))
答案 2 :(得分:0)
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+", " ")
此示例应返回所需的输出。并且很容易为其他元素添加案例,注释现有案例等等。