计算XSLT中引用的引用

时间:2016-11-23 08:25:43

标签: xml recursion count xls saxon

我想我需要你的帮助。

我有一个以这种方式构建的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 个答案:

答案 0 :(得分:1)

您需要问自己的第一个问题是:是否需要检测数据中的循环,或者您是否已准备好让程序在循环存在时进入无限循环?检测循环会使问题变得更加困难,但是一旦基本结构正常工作,它就可以轻松添加。

接下来,我可能误解了确切的要求,因为我无法看到源节点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))

剩下的就是循环检测。要实现此目的,请向函数添加一个额外参数,其中包含所有节点“途中”到您正在查找其连接的节点,并使用“except”以避免跟踪来自已经“途中”的任何节点的链接。

<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>