计算 XSLT 中引用的引用

Counting references of references in XSLT

我想我需要你的帮助。

我有一个 XML 文件结构如下:

<root>
    <source id="1"/>
    <source id="2"/>
    <source ..... />
    <element id="e1">
        <connection from_id="1">
    </element>
    <element id="e7">
        <connection from_id="1">
    </element>
    <element id="e2">
        <connection from_id="e2">
    </element>
    <element id="e3">
        <connection from_id="e2">
    </element>
    <element id="e4">
        <connection from_id="e3">
    </element>
    <element id="e5">
        <connection from_id="2">
    </element>
    <element id="e6">
        <connection from_id="3">
    </element>
</root>

现在,我试图完成的是 count() 以某种方式连接到每个源的节点数(更具体地说:元素节点)-节点,即使它们通过另一个元素节点连接。所以对于这个例子: 源节点 1: 5 源节点 2: 1 源节点 3: 1

如果尝试了多种方法,包括函数和递归,但我无法完成这项任务。从我每天 java 程序员的角度来看,我只是错过了一个变量左右来保存一些中间结果。

所以我的问题是:如何在没有任何中间结果的情况下执行此操作?

您需要问自己的第一个问题是:是否需要检测数据中的循环,或者如果循环存在,您是否准备让程序进入无限循环?检测周期使问题变得有点困难,但一旦基本结构正常工作就可以很容易地添加它。

接下来,我可能误解了确切的要求,因为我看不到源节点 1 的计数是如何达到 5 的。正如我所看到的,元素 e1 和 e7 连接到源节点 1 而这些都不是有任何进一步的联系。

您没有说它是 XSLT 1.0 还是 2.0,但是由于问题上有一个 "Saxon" 标记,我们假设 2.0(这比 1.0 容易得多)。这里正确的标签应该是 "xslt 2.0" 而不是 "saxon" 因为问题不是撒克逊特有的。

你基本上想从一个函数开始

<xsl:function name="f:directConnections" as="element()*">
  <xsl:param name="from" as="element()"/>
  <xsl:sequence select="key('with-from-id', $from/@id, $from/root())"/>
</xsl:function>

已申报

<xsl:key name="with-from-id" match="connection" select="@from_id"/>

那么这个函数的传递闭包就是:

<xsl:function name="f:transitiveConnections" as="element()*">
  <xsl:param name="from" as="element()"/>
  <xsl:variable name="direct" select="f:directConnections($from)"/>
  <xsl:sequence select="$direct/(. | f:transitiveConnections(.))"/>
</xsl:function>

然后给定节点 $N 的连接数为 count(f:transitiveConnections($N))

剩下的就是循环检测了。为此,向包含所有节点的函数添加一个额外参数 'en route' 到您正在查找其连接的节点,并使用 "except" 以避免从任何已经 [=35= 的节点跟踪链接].

<xsl:function name="f:transitiveConnections" as="element()*">
  <xsl:param name="from" as="element()"/>
  <xsl:param name="enRoute" as="element()*"/>
  <xsl:variable name="direct" 
                select="f:directConnections($from) except $enRoute"/>
  <xsl:sequence select="$direct/(. | f:transitiveConnections(., $enRoute | .))"/>
</xsl:function>