如何使用XSL统计有向无环图中的子节点数量
嘿,我来帮你搞定用XSL统计有向无环图(DAG)子节点数量的问题。首先咱们得明确需求:你是想统计每个节点的直接子节点数量(只算下一级),还是递归统计该节点下所有的后代节点总数(包含子节点、孙节点等所有层级)?我两种场景都给你准备了示例代码,你可以按需选用。
先补全你提供的XML(假设截断的item7父节点是2,完整结构如下):
<?xml version="1.0" encoding="utf-8"?> <items> <item id="1"> </item> <item id="2"> <parent idref="1" /> </item> <item id="3"> <parent idref="1" /> </item> <item id="4"> <parent idref="3" /> </item> <item id="5"> <parent idref="4" /> </item> <item id="6"> <parent idref="3" /> </item> <item id="7"> <parent idref="2" /> </item> </items>
1. 统计每个节点的直接子节点数量
这个需求比较简单,核心是用<xsl:key>建立父节点ID到子节点的映射,然后对每个节点统计匹配的子节点数量即可。下面是XSLT 1.0的实现(兼容性最好):
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 建立父ID到对应子节点的索引,方便快速查询 --> <xsl:key name="child-by-parent" match="item" use="parent/@idref"/> <xsl:template match="/items"> <node-stats> <!-- 遍历所有节点,逐个统计直接子节点数 --> <xsl:for-each select="item"> <node id="{@id}"> <direct-children-count> <!-- 统计当前ID作为父ID的节点数量 --> <xsl:value-of select="count(key('child-by-parent', @id))"/> </direct-children-count> </node> </xsl:for-each> </node-stats> </xsl:template> </xsl:stylesheet>
运行后你会得到类似这样的输出:
<node-stats> <node id="1"> <direct-children-count>2</direct-children-count> </node> <node id="2"> <direct-children-count>1</direct-children-count> </node> <node id="3"> <direct-children-count>2</direct-children-count> </node> <!-- 其他节点的统计结果省略 --> </node-stats>
2. 递归统计每个节点的所有后代节点总数
如果需要统计某个节点下所有层级的后代(比如节点1的后代包含2、3、4、5、6、7),就得用递归模板来实现。同样借助<xsl:key>来快速查找子节点,然后递归累加每个子节点的后代数:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="child-by-parent" match="item" use="parent/@idref"/> <!-- 递归模板:计算传入节点ID的所有后代总数 --> <xsl:template name="count-descendants"> <xsl:param name="node-id"/> <!-- 获取当前节点的所有直接子节点 --> <xsl:variable name="direct-children" select="key('child-by-parent', $node-id)"/> <!-- 先统计直接子节点数,再累加每个子节点的后代数 --> <xsl:variable name="child-descendant-counts"> <xsl:for-each select="$direct-children"> <count> <xsl:call-template name="count-descendants"> <xsl:with-param name="node-id" select="@id"/> </xsl:call-template> </count> </xsl:for-each> </xsl:variable> <xsl:value-of select="count($direct-children) + sum($child-descendant-counts/count)"/> </xsl:template> <xsl:template match="/items"> <node-stats> <xsl:for-each select="item"> <node id="{@id}"> <total-descendants-count> <xsl:call-template name="count-descendants"> <xsl:with-param name="node-id" select="@id"/> </xsl:call-template> </total-descendants-count> </node> </xsl:for-each> </node-stats> </xsl:template> </xsl:stylesheet>
这个模板运行后,节点1的total-descendants-count会是6(所有子节点加后代),节点3的总数是3(4、5、6),完全符合DAG的层级关系。
内容的提问来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

