Tengo un archivo XML que codifica directed acyclic graph (DAG) que representa un partial order. Dichos gráficos son útiles para cosas como especificar dependencias y encontrar critical paths. Para los curiosos, mi aplicación actual es especificar dependencias de componentes para un build system, por lo que los vértices son componentes y los bordes especifican las dependencias en tiempo de compilación. Aquí está un ejemplo sencillo:¿Cómo encontrar gráficos acíclicos dirigidos (DAG) Elementos mínimos (vértices) con XSLT/XPath?
<?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>
Este DAG puede extraerse de esta manera:
me gustaría aplicar un XSLTstylesheet que produce otro documento XML que contiene sólo los vértices que corresponden a minimal elements del orden parcial. Es decir, aquellos vértices que no tienen bordes entrantes. El conjunto de vértices mínimos para el gráfico de ejemplo es {A, B, F}
. Para mi aplicación de dependencia de compilación, encontrar este conjunto es valioso porque sé que si construyo los miembros de este conjunto, se construirá todo en mi proyecto.
Aquí está mi solución de hoja de estilo actual (estoy ejecutando esto con Xalan en Java usando la tarea xslt
de Apache Ant). Una observación clave es que un vértice mínimo no se hace referencia en cualquier elemento 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>
La aplicación de esta hoja de estilo produce la siguiente salida (que creo que es correcta):
<?xml version="1.0" encoding="UTF-8"?>
<minimal-vertices>
<minimal-vertex name="A"/>
<minimal-vertex name="B"/>
<minimal-vertex name="F"/>
</minimal-vertices>
la cosa es, No estoy completamente satisfecho con esta solución. Me pregunto si hay una manera de combinar el select
del for-each
y el test
del if
con sintaxis XPath.
Quiero escribir algo como:
<xsl:for-each select="//vertex[not(//vertex/directed-edge-to[@vertex=current()/@name])]">
Pero eso no quiere hacer lo que quiera porque la función current()
no hace referencia a los nodos seleccionados por el //vertex
expresión externa.
Thusfar, mi solución utiliza XPath 1.0 y XSLT 1.0 sintaxis, aunque estoy abierto a XPath 2.0 y XSLT 2.0 sintaxis también.
Aquí está el script de construcción Ant si te gusta:
<?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>
El objetivo dot
genera código GraphvizDotlanguage para representar el gráfico.Aquí es 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>
La abreviatura "//" debe evitarse siempre que sea posible, ya que es muy cara, lo que hace que se rastree todo el subárbol enraizado en el nodo de contexto. "//" en el nivel superior hace que se busque todo el documento XML. Es muy importante * no * usar "//" siempre que se conozca la estructura del documento XML en el momento de escribir la expresión XPath –