我正在尝试使用 XSLT 从大型 XML 中呈现一些数据。 XML 数据实际上是一种图形数据,而不是分层的。和元素彼此相关,因此最终可能会出现循环引用(但是关系类型不同)。
我试图遍历一个元素的关系并访问每个相关元素等等。这样,有时我会到达一个我已经遍历过的元素。在这种情况下,我应该停止进一步遍历,否则我将在一个循环中运行。
我的问题是,我无法存储我已经遍历过的元素列表,也无法在每次开始遍历元素时进行查找,这样如果该元素在查找中,我就可以停止遍历。
简单地说,我想将元素保存在一个查找表中,并在遍历时将每个元素添加到其中。
有什么解决办法吗?
最佳答案
递归模板可以传递自己的参数,这些参数包含“先前”处理过的节点的节点集和要处理的节点队列。这是一个等同于修改状态变量的函数式编程。
示例输入:
<graph startNode="a">
<graphNode id="a">
<edge target="b" />
<edge target="c" />
</graphNode>
<graphNode id="b">
<edge target="c" />
</graphNode>
<graphNode id="c">
<edge target="d" />
</graphNode>
<graphNode id="d">
<edge target="a" />
<edge target="b" />
</graphNode>
</graph>
XSL 2.0 样式表:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="graphNodeByID" match="graphNode" use="@id" />
<xsl:template match="/graph">
<results>
<xsl:apply-templates select="key('graphNodeByID', @startNode)"
mode="process"/>
</results>
</xsl:template>
<xsl:template match="graphNode" mode="process">
<xsl:param name="already-processed" select="/.." />
<xsl:param name="queue" select="/.." />
<!-- do stuff with context node ... -->
<processing node="{@id}" />
<!-- Add connected nodes to queue, excluding those already processed. -->
<xsl:variable name="new-queue"
select="($queue | key('graphNodeByID', edge/@target))
except ($already-processed | .)" />
<!-- recur on next node in queue. -->
<xsl:apply-templates select="$new-queue[1]" mode="process">
<xsl:with-param name="already-processed"
select="$already-processed | ." />
<xsl:with-param name="queue" select="$new-queue" />
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
输出(测试):
<results>
<processing node="a"/>
<processing node="b"/>
<processing node="c"/>
<processing node="d"/>
</results>
按照规定,没有节点被处理两次,即使图中包含循环。
关于xml - XSLT:检查元素是否较早遍历,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8605118/