Search code examples
xmlxsltgraphml

matching a subset of nodes using xsl


I've got the following graph:

<?xml version="1.0" encoding="utf-8"?><graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <graph edgedefault="undirected">
    <node id="a">
        <data key="d0">some info</data>
    </node>
    <node id="b"/>
    <node id="c">
        <data key="d0">some more info</data>
    </node>
    <node id="d"/>
    <edge source="a" target="b"/>
    <edge source="a" target="c"/>
    <edge source="b" target="c"/>
    <edge source="b" target="d"/>
    <edge source="c" target="d"/>
 </graph>
</graphml>

And I'm trying to use XSLT to create a subset of the graph containing all nodes neighboring node a.

Desired output:

<?xml version="1.0" encoding="utf-8"?><graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <graph edgedefault="undirected">
    <node id="b"/>
    <node id="c">
        <data key="d0">some more info</data>
    </node>
    <edge source="b" target="c"/>
 </graph>
</graphml>

I'm not a real expert of XSLT, but is it possible to do so in steps? i.e. first removing problematic edges, then removing neighborless nodes?


Solution

  • Here is an XSLT 2.0 (can be run with Saxon 9, Altova, XmlPrime, Exselt) based solution using keys:

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:transform
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        version="2.0"
        xpath-default-namespace="http://graphml.graphdrawing.org/xmlns">
    
        <xsl:output indent="yes"/>
    
        <xsl:param name="start-id" as="xs:string" select="'a'"/>
    
        <xsl:key name="node-id" match="node" use="@id"/>
    
        <xsl:key name="source-edge" match="edge" use="@source"/>
        <xsl:key name="target-edge" match="edge" use="@target"/>
    
        <xsl:variable name="start-node" select="key('node-id', $start-id)"/>
    
        <xsl:variable name="neighbours"
                      select="key('node-id', key('source-edge', $start-node/@id)/@target)
                              | key('node-id', key('target-edge', $start-node/@id)/@source)"/>
    
        <xsl:variable name="neighbour-edges"
                      select="//edge[@source = $neighbours/@id and @target = $neighbours/@id]"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="graph">
            <xsl:copy>
                <xsl:copy-of select="$neighbours | $neighbour-edges"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:transform>