You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在XSL中获取有向无环图中关联元素的所有路径?

如何获取有向无环图中的所有路径?

问题背景

我拥有一组元素和一组带有sourceRef与targetRef属性的有向链接,构成一个有向无环图(directed acyclic graph,DAG)。需要获取该图中的所有路径。

输入XML示例

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <element id="a" />
    <element id="b" />
    <element id="c" />
    <element id="d" />
    <element id="e" />

    <link sourceRef="a" targetRef="b" />
    <link sourceRef="b" targetRef="c" />

    <link sourceRef="a" targetRef="d" />
    <link sourceRef="d" targetRef="c" />

    <link sourceRef="a" targetRef="e" />

</root>

期望输出XML

<paths>
    <path>
        <element id="a" />
        <element id="b" />
        <element id="c" />
    </path>
    <path>
        <element id="a" />
        <element id="d" />
        <element id="c" />
    </path>
    <path>
        <element id="a" />
        <element id="e" />
    </path>
</paths>

XSLT 1.0实现方案

以下是基于递归遍历的XSLT 1.0解决方案,通过追踪路径节点避免循环(因输入是DAG,实际不会出现循环,但逻辑上保留防循环处理):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="element-by-id" match="element" use="@id" />
<xsl:key name="outgoing-links" match="link" use="@sourceRef" />

<xsl:template match="/root">
    <paths>
        <!-- 遍历所有起始节点(无入度的节点) -->
        <xsl:apply-templates select="element[not(key('outgoing-links', @id)/@targetRef = current()/@id)]">
            <xsl:with-param name="path" select="." />
        </xsl:apply-templates>
    </paths>
</xsl:template>

<xsl:template match="element">
    <xsl:param name="path" />
    <!-- 检查当前节点是否已在路径中(防循环) -->
    <xsl:if test="not($path/element/@id = @id)">
        <xsl:variable name="new-path" select="$path | ." />
        <!-- 获取当前节点的所有出链目标 -->
        <xsl:variable name="targets" select="key('outgoing-links', @id)/@targetRef" />
        <xsl:choose>
            <!-- 如果没有出链,输出完整路径 -->
            <xsl:when test="not($targets)">
                <path>
                    <xsl:copy-of select="$new-path" />
                </path>
            </xsl:when>
            <!-- 否则递归遍历每个目标节点 -->
            <xsl:otherwise>
                <xsl:for-each select="key('element-by-id', $targets)">
                    <xsl:apply-templates select=".">
                        <xsl:with-param name="path" select="$new-path" />
                    </xsl:apply-templates>
                </xsl:for-each>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

XSLT 2.0实现方案

XSLT 2.0支持更简洁的递归和序列处理,以下是优化后的实现:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="element-by-id" match="element" use="@id" />
<xsl:key name="outgoing-links" match="link" use="@sourceRef" />

<xsl:template match="/root">
    <paths>
        <!-- 处理所有起始节点 -->
        <xsl:for-each select="element[not(exists(key('outgoing-links', @id)/@targetRef = current()/@id))]">
            <xsl:call-template name="build-path">
                <xsl:with-param name="current-node" select="." />
                <xsl:with-param name="current-path" select="." />
            </xsl:call-template>
        </xsl:for-each>
    </paths>
</xsl:template>

<xsl:template name="build-path">
    <xsl:param name="current-node" as="element(element)" />
    <xsl:param name="current-path" as="element(element)*" />
    
    <xsl:variable name="target-ids" select="key('outgoing-links', $current-node/@id)/@targetRef" />
    <xsl:if test="empty($target-ids)">
        <path>
            <xsl:copy-of select="$current-path" />
        </path>
    </xsl:if>
    
    <!-- 递归处理每个目标节点 -->
    <xsl:for-each select="key('element-by-id', $target-ids)">
        <xsl:call-template name="build-path">
            <xsl:with-param name="current-node" select="." />
            <xsl:with-param name="current-path" select="$current-path, ." />
        </xsl:call-template>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

方案说明

  • 两个版本均通过<xsl:key>建立元素ID和出链的索引,提升查询效率
  • 从无入度的起始节点开始遍历,递归追踪每个节点的出链目标
  • 当节点没有出链时,将当前累积的路径输出为<path>元素
  • 因输入是有向无环图,无需额外处理循环,但代码中保留了防循环逻辑(XSLT 1.0版本通过检查节点是否已在路径中实现)

内容的提问来源于stack exchange,提问作者64kilobit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 10:05:40