我想我需要你的帮助。
我有一个以这种方式构建的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程序员角度来看,我只是想念一个变量左右来保存一些中间结果。
所以我的问题:如何在没有任何中间结果的情况下做到这一点?
答案 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>