【发布时间】:2010-10-25 00:56:05
【问题描述】:
我有一个 XML 文件,它编码一个 directed acyclic graph (DAG) 表示 partial order。这样的图表对于指定依赖项和查找critical paths 等事情很有用。出于好奇,我当前的应用程序是为 build system 指定组件依赖项,因此顶点是组件,边指定编译时依赖项。这是一个简单的例子:
<?xml version="1.0"?>
<dag>
<vertex name="A">
<directed-edge-to vertex="C"/>
</vertex>
<vertex name="B">
<directed-edge-to vertex="C"/>
<directed-edge-to vertex="D"/>
</vertex>
<vertex name="C">
<directed-edge-to vertex="E"/>
</vertex>
<vertex name="D">
<directed-edge-to vertex="E"/>
</vertex>
<vertex name="E">
<directed-edge-to vertex="G"/>
</vertex>
<vertex name="F">
<directed-edge-to vertex="G"/>
</vertex>
<vertex name="G"/>
</dag>
这个 DAG 可以这样绘制:
(来源:iparelan.com)
我想应用一个 XSLT stylesheet 来生成另一个 XML
仅包含与偏序的minimal elements 对应的顶点的文档。也就是说,那些没有传入边的顶点。示例图的最小顶点集是{A, B, F}。对于我的构建依赖应用程序,找到这个集合很有价值,因为我知道如果我构建这个集合的成员,那么我的项目中的所有内容都将被构建。
这是我当前的样式表解决方案(我使用 Apache Ant 的 xslt 任务在 Java 上使用 Xalan 运行它)。一个关键的观察是在任何directed-edge-to 元素中都不会引用最小顶点:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xslt"
exclude-result-prefixes="xalan">
<xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>
<xsl:template match="dag">
<minimal-vertices>
<xsl:for-each select="//vertex">
<xsl:if test="not(//vertex/directed-edge-to[@vertex=current()/@name])">
<minimal-vertex name="{@name}"/>
</xsl:if>
</xsl:for-each>
</minimal-vertices>
</xsl:template>
</xsl:stylesheet>
应用此样式表会产生以下输出(我认为这是正确的):
<?xml version="1.0" encoding="UTF-8"?>
<minimal-vertices>
<minimal-vertex name="A"/>
<minimal-vertex name="B"/>
<minimal-vertex name="F"/>
</minimal-vertices>
问题是,我对这个解决方案并不完全满意。 我想知道是否有办法将for-each 的select 和if 的test 与XPath 语法结合起来。
我想写这样的东西:
<xsl:for-each select="//vertex[not(//vertex/directed-edge-to[@vertex=current()/@name])]">
但这并不符合我的要求,因为current() 函数没有引用外部//vertex 表达式选择的节点。
到目前为止,我的解决方案使用 XPath 1.0 和 XSLT 1.0 语法,尽管我也对 XPath 2.0 和 XSLT 2.0 语法持开放态度。
如果您愿意,这里是 Ant 构建脚本:
<?xml version="1.0"?>
<project name="minimal-dag" default="default">
<target name="default">
<xslt in="dag.xml" out="minimal-vertices.xml" style="find-minimal-vertices.xsl"/>
</target>
<target name="dot">
<xslt in="dag.xml" out="dag.dot" style="xml-to-dot.xsl"/>
</target>
</project>
dot 目标生成 Graphviz Dot language 代码用于渲染图形。这里是xml-to-dot.xsl:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xslt"
exclude-result-prefixes="xalan">
<xsl:output method="text"/>
<xsl:template match="dag">
digraph {
rankdir="BT";
node [style="filled", fillcolor="cyan", fontname="Helvetica"];
<xsl:apply-templates select="//directed-edge-to"/>
}
</xsl:template>
<xsl:template match="directed-edge-to">
<xsl:value-of select="concat(ancestor::vertex/@name, '->', @vertex, ';')"/>
</xsl:template>
</xsl:stylesheet>
【问题讨论】:
-
应尽可能避免使用“//”缩写,因为它非常昂贵,会导致搜索以上下文节点为根的整个子树。顶层的“//”导致搜索整个 XML 文档。在编写 XPath 表达式时,只要 XML 文档的结构已知,就不要使用“//”是非常重要的
标签: xslt xpath graph-theory directed-acyclic-graphs build-system