【问题标题】:XQuery ancestor axis doesn't work, but explicit XPath doesXQuery 祖先轴不起作用,但显式 XPath 可以
【发布时间】:2012-02-01 16:37:08
【问题描述】:

考虑以下XMLsn-p:

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

XQuery,我有一个函数需要根据作为参数传入的给定“para”元素的ancestor章节做一些事情,如剥离下面的例子:

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 起作用?另外,有人对如何解决这个问题有任何建议吗?

谢谢。


解决方案:

谜团现已解开。

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

因此,祖先轴完全按照应有的方式工作 - 它只是被应用于欺骗性节点。

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

【问题讨论】:

    标签: xpath xquery marklogic axes


    【解决方案1】:

    祖先轴工作正常。我怀疑您的问题是名称空间。您展示的示例和我运行的示例(如下)包含没有任何名称空间的 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>
    

    【讨论】:

    • 谢谢你,克拉克。我已经通过 MarkLogic 4.2 中的错误日志验证了 $para 是我所期望的。我知道它应该起作用,但由于某种原因它不起作用。这里正在发生很多令人头疼的事情。 :^)
    • 我怀疑你的问题是命名空间。您展示的示例和我运行的示例包含没有任何名称空间的 XML。如果您的 XML 实际上有一个命名空间,那么您需要在祖先 XPath 中提供它,如下所示: $para/ancestor:foo:chapter 在这种情况下,前缀 foo 绑定到章节元素的正确命名空间。
    • @mahdaeng 或者,您可能在模块顶部为某些特定命名空间声明了默认元素命名空间,而您的 XML 没有或不同。
    • 谢谢你们,先生们,在这里提到命名空间可能会导致问题。但是,在本文档中,没有使用命名空间。
    【解决方案2】:

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

    declare function local:doSomething($para) {
        let $chapter := $para/ancestor::*[local-name() = 'chapter']
        return $chapter
    };
    

    【讨论】:

      【解决方案3】:

      这让我感到惊讶;您使用的是哪个 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>
      

      【讨论】:

      • 谢谢你,克里斯蒂安。我正在使用 MarkLogic 4.2 提供的任何 XQuery 实现。
      • 是的,我同意使用在 MarkLogic 5 上运行的 developer.marklogic.com/try/ninja/page2 的查询评估器可以正常工作。您使用的是哪个版本的 4.2?
      • 我听说上面的代码在 4.2-4 中运行良好,所以环境可能会让你感到困惑。
      【解决方案4】:

      我也怀疑命名空间。如果$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 只会选择不在命名空间中的&lt;chapter&gt; 元素(除非您已声明默认元素命名空间,这在 XQuery 中通常不是一个好主意,因为它会影响您的输入和输出)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多