0

考虑以下XML代码段:

<doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>

XQuery中,我有一个函数需要根据作为参数传入的给定“para”元素的祖先章节做一些事情,如下面的精简示例所示:

declare function doSomething($para){
    let $chapter := $para/ancestor::chapter
    return "some stuff"
};

在那个例子中,$chapter一直是空的。但是,如果我编写类似于以下的函数(即,不使用祖先轴),我会得到所需的“章节”元素:

declare function doSomething($para){
    let $chapter := $para/../..
    return "some stuff"
};

问题是我不能像后一个示例中那样使用显式路径,因为XMl我将搜索的不能保证每次都将“章节”元素作为祖父母。可能是曾祖父母或曾曾祖父母,以此类推,如下图:

<doc>
    <chapter id="1">
        <item>
            <subItem>
                <para>some text here</para>
            </subItem>
        </item>
    </chapter>
</doc>

有没有人解释为什么轴不起作用,而显式 XPath 起作用?另外,有没有人对如何解决这个问题有任何建议?

谢谢你。


解决方案:

现在谜团解开了。

有问题的节点是在另一个函数中重新创建的,结果是剥离了它的所有祖先信息。不幸的是,以前的开发人员没有记录这个美妙的小功能,并且花费了我们所有人的大量时间。

所以,祖先轴完全按照它应该的方式工作——它只是被应用于一个欺骗性的节点。

我感谢大家为回答我的问题所做的努力。

4

4 回答 4

4

祖先轴工作正常。我怀疑您的问题是名称空间。您展示的示例和我运行的示例(如下)包含没有任何名称空间的 XML。如果您的 XML 有一个命名空间,那么您需要在祖先XPath中提供它,如下所示:$para/ancestor:foo:chapter在这种情况下,prefix _foo_ is绑定到章节元素的正确命名空间。

let $doc := <doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>

let $para := $doc//para
return $para/ancestor::chapter

结果:

<?xml version="1.0" encoding="UTF-8"?>
<chapter id="1">
  <item>
    <para>some text here</para>
  </item>
</chapter>
于 2012-01-03T17:06:53.067 回答
2

这些东西几乎总是归结为命名空间!作为 100% 确认命名空间不是问题的诊断,您可以尝试:

declare function local:doSomething($para) {
    let $chapter := $para/ancestor::*[local-name() = 'chapter']
    return $chapter
};
于 2012-01-03T20:28:00.280 回答
1

这让我感到惊讶。您使用的是哪个 XQuery 实现?使用 BaseX,以下查询...

declare function local:doSomething($para) {
    let $chapter := $para/ancestor::chapter
    return $chapter
};

let $xml :=
  <doc>
    <chapter id="1">
        <item>
            <para>some text here</para>
        </item>
    </chapter>
</doc>
return local:doSomething($xml//para)

...返回...

<chapter id="1">
  <item>
    <para>some text here</para>
  </item>
</chapter>
于 2012-01-03T17:04:45.353 回答
1

我也怀疑命名空间。如果$para/../..有效但$para/parent::item/parent::chapter结果为空,那么您知道这是名称空间的问题。

在内容顶部查找 xmlns 声明,例如:

<doc xmlns="http://example.com">
  ...
</doc>

在您的 XQuery 中,您需要将该名称空间绑定到一个前缀,并在您的 XQuery/XPath 表达式中使用该前缀,如下所示:

declare namespace my="http://example.com";
declare function doSomething($para){
  let $chapter := $para/ancestor::my:chapter
  return "some stuff"
};

您使用什么前缀无关紧要。重要的是名称空间 URI(http://example.com在上面的示例中)匹配。

../..选择您想要的元素是有意义的,因为它..是选择父节点的缩写,parent::node()无论其名称(或命名空间)如何。而ancestor::chapter只会选择<chapter>不在名称空间中的元素(除非您声明了默认元素名称空间,这在 XQuery 中通常不是一个好主意,因为它会影响您的输入和输出)。

于 2012-01-03T19:07:26.580 回答