【问题标题】:XPath to navigate graph structure encoded in XMLXPath 导航以 XML 编码的图形结构
【发布时间】:2015-04-16 13:41:21
【问题描述】:

我有一个用 XML 编码的类似图形的结构。准确地说,有向无环图。 这些节点是唯一标识的,并具有编码的后继关系。

是否可以构造一个 XQuery 或 XPath 函数来导航图以找到满足简单条件的第一个节点?

更准确地说,我们来看下面的 XML 文档:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <obj>
        <id>1</id>
        <name>FINDME</name>
        <relations>
            <successor>2</successor>
        </relations>
    </obj>
    <obj>
        <id>2</id>
        <relations>
            <predecessor>1</predecessor>
            <successor>3</successor>
        </relations>
    </obj>
    <obj>
        <id>3</id>
        <relations>
            <predecessor>2</predecessor> 
        </relations>
    </obj>
</doc>

假设,当前节点是 3。 我想找到一个前驱节点的 id,它的名称标签等于“FINDME”。 为此,我可以使用以下 XPath:

/doc/obj[name='FINDME'
      and id=/doc/obj[
          id=/doc/obj[
               id=3
               ]/relations/predecessor
           ]/relations/predecessor
       ]/id

是否可以泛化查询以处理任意级别的关系?


编辑:joemfb 的答案确实回答了这个问题。我有一个额外的请求,它是否可以为一个节点处理多个前驱节点并且不超过一次返回任何节点?

因此,它也适用于以下示例:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <obj>
        <id>1</id>
        <name>FINDME</name>
        <relations>
            <successor>2</successor>
            <successor>4</successor>
        </relations>
    </obj>
    <obj>
        <id>4</id>
        <relations>
            <successor>2</successor>
            <predecessor>1</predecessor>
        </relations>
    </obj>
    <obj>
        <id>2</id>
        <relations>
            <predecessor>1</predecessor>
            <predecessor>4</predecessor>
            <successor>3</successor>
        </relations>
    </obj>
    <obj>
        <id>3</id>
        <relations>
            <predecessor>2</predecessor> 
        </relations>
    </obj>
</doc>

【问题讨论】:

  • 任意级别的关系是什么意思?结构的哪一部分是任意的,或者任意嵌套的?
  • 我作为示例提供的 XPath 查询只会找到所提供节点的前任的前任...我想处理“所有前任”

标签: xml xpath xquery


【解决方案1】:

如果我正确理解你的要求,你想找到前辈的前辈,只要他们存在。这是递归函数的完美应用。

这是一个 XQuery 解决方案:

xquery version "1.0";

declare function local:predecessors($doc, $node)
{
  let $immediate-predecessor := $doc/doc/obj[id = $node/relations/predecessor]
  return
    if (fn:exists($immediate-predecessor))
    then (
      $immediate-predecessor,
      local:predecessors($doc, $immediate-predecessor)
    )
    else ()
};

let $doc := document { (: your doc ... :) }
let $results := local:predecessors($doc, $doc/doc/obj[id eq "3"])
return element results { $results intersect $results }

更新

为了支持多个predecessor 元素,谓词[id = $node/relations/predecessor] 需要通用比较运算符=,它支持任一操作数中的序列,而不是值比较运算符eq

要返回不同的节点,我们可以使用intersect operator,它返回两个操作数共有的唯一节点序列(我也可以使用union,它同样返回唯一节点)。

我已经在 Saxon HE 中测试了这些变化:

java -cp /usr/local/Cellar/saxon/9.5.1.6/libexec/saxon9he.jar \
  net.sf.saxon.Query -q:test.xq | xmllint --format -

【讨论】:

  • 我想,这正好回答了我的问题!我可以从这个开始。如果我在我的真实图表上运行它,我会得到 XPTY0004:一个以上项目的序列不允许作为“eq”的第二个操作数。你能帮我解决这个问题吗?
  • ...我认为如果我将“eq”直接替换为“=”运算符,它会起作用。
  • 但话又说回来,它两次返回 id 为 1 的节点!有没有可能让它与众不同?
  • 您使用的是什么 XPath/XQuery 处理器?
  • 我正在使用带有 Saxon 引擎的 Oxygen XML Developer(运行标准 XQuery 1.0 查询的引擎)
猜你喜欢
  • 1970-01-01
  • 2019-12-17
  • 2015-02-07
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多