XQuery中的空白处理不一致?

时间:2015-02-27 15:06:35

标签: xml xquery

我很困惑如何让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;被忽略。

3 个答案:

答案 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+", " ")

此示例应返回所需的输出。并且很容易为其他元素添加案例,注释现有案例等等。