XSLT:检查Element是否先前遍历过

Kan*_*kan 2 xml xslt xpath xslt-2.0

我试图使用XSLT从大型XML渲染一些数据.XML数据实际上是一种图形数据而不是分层数据.和元素彼此相关,因此最终可能会有一个循环引用(但关系类型却不同).

我试图遍历一个元素的关系并访问每个相关元素,依此类推.通过这种方式,有时我会达到一个我已经遍历过的元素.在这种情况下,我应该停止进一步遍历,否则我将在一个循环中运行.

我的问题是我无法存储已经遍历的元素列表,并且每次开始遍历元素时都会查找,这样如果元素在查找中,我就可以停止遍历.

简单地说,我想将元素保存在查找表中,并在遍历时将每个元素添加到它中.

这有什么解决方案吗?

Lar*_*rsH 7

递归模板可以传递自身参数,该参数包含"先前"处理的节点的节点集和要处理的节点的队列.这是一个与修改状态变量等效的函数式编程.

样本输入:

<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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

输出(测试):

<results>
   <processing node="a"/>
   <processing node="b"/>
   <processing node="c"/>
   <processing node="d"/>
</results>
Run Code Online (Sandbox Code Playgroud)

如指定的那样,即使图形包含循环,也不会处理两次节点.

  • Lars,我在你和我的解决方案中都发现了一个小问题,并在我的回答中纠正了这个问题.要重新生成,只需将此图形节点添加到XML:`<graphNode id ="e"> <edge target ="a"/> </ graphNode>`然后运行转换.正如您将看到的,节点"e"不会遍历并包含在输出中. (2认同)