For example, imagine a relationship of elements describing animals
starting with "animal" at the top of the hierarchy and ending in "pets"
at the bottom:
animal => class => beastie => breed => pet
Given the xml below, say I wanted to remove the class named "Aves" and
wanted to cause a cascading removal of all other elements related to
"Aves". Is there a simple way to do this using xsl that helps me avoid
writing specific templates for each child node of "animals"?
<?xml version="1.0" encoding="utf-8"?>
<animals>
<classes>
<class name="Mammalia"/>
<class name="Aves"/>
</classes>
<beasties>
<beast type="cat" class="Mammalia"/>
<beast type="dog" class="Mammalia"/>
<beast type="bird" class="Aves"/>
</beasties>
<breeds>
<breed name="collie" type="dog"/>
<breed name="beagle" type="dog"/>
<breed name="persian" type="cat"/>
<breed name="siamese" type="cat"/>
<breed name="parakeet" type="bird"/>
<breed name="crow" type="bird"/>
</breeds>
<pets>
<pet name="rover" breed="collie"/>
<pet name="fluffy" breed="persian"/>
<pet name="tweety" breed="parakeet"/>
</pets>
</animals>
The desired xml output would be this:
<?xml version="1.0" encoding="utf-8"?>
<animals>
<classes>
<class name="Mammalia"/>
</classes>
<beasties>
<beast type="cat" class="Mammalia"/>
<beast type="dog" class="Mammalia"/>
</beasties>
<breeds>
<breed name="collie" type="dog"/>
<breed name="beagle" type="dog"/>
<breed name="persian" type="cat"/>
<breed name="siamese" type="cat"/>
</breeds>
<pets>
<pet name="rover" breed="collie"/>
<pet name="fluffy" breed="persian"/>
</pets>
</animals>
XSLT 1.0 solution (49 lines):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pClassToDel" select="'Aves'"/>
<xsl:key name="kBeastByType" match="beast"
use="@type"/>
<xsl:key name="kBreedByName" match="breed"
use="@name"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="class">
<xsl:if test="not(@name = $pClassToDel)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="beast">
<xsl:if test="not(@class = $pClassToDel)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="breed">
<xsl:if test=
"not(key('kBeastByType', @type)/@class = $pClassToDel)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="pet">
<xsl:if test=
"not(key('kBeastByType',
key('kBreedByName', @breed)/@type
)/@class
=
$pClassToDel)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<animals>
<classes>
<class name="Mammalia" />
</classes>
<beasties>
<beast type="cat" class="Mammalia" />
<beast type="dog" class="Mammalia" />
</beasties>
<breeds>
<breed name="collie" type="dog" />
<breed name="beagle" type="dog" />
<breed name="persian" type="cat" />
<breed name="siamese" type="cat" />
</breeds>
<pets>
<pet name="rover" breed="collie" />
<pet name="fluffy" breed="persian" />
</pets>
</animals>
XSLT 2.0 solution (40 lines):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="xs"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pClassToDel" select="'Aves'"/>
<xsl:key name="kDeleted" match="beast"
use="string(@class = $pClassToDel)"/>
<xsl:key name="kDeleted" match="class"
use="string(@name = $pClassToDel)"/>
<xsl:key name="kDeleted" match="breed"
use="string(key('kBeastByType',@type)/@class = $pClassToDel)"/>
<xsl:key name="kDeleted" match="pet"
use="string(key('kBeastByType',
key('kBreedByName',@breed)/@type
)
/@class
=
$pClassToDel)"/>
<xsl:key name="kBeastByType" match="beast"
use="@type"/>
<xsl:key name="kBreedByName" match="breed"
use="@name"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[. intersect key('kDeleted', 'true')]"/>
</xsl:stylesheet>
Output:
<animals>
<classes>
<class name="Mammalia"/>
</classes>
<beasties>
<beast type="cat" class="Mammalia"/>
<beast type="dog" class="Mammalia"/>
</beasties>
<breeds>
<breed name="collie" type="dog"/>
<breed name="beagle" type="dog"/>
<breed name="persian" type="cat"/>
<breed name="siamese" type="cat"/>
</breeds>
<pets>
<pet name="rover" breed="collie"/>
<pet name="fluffy" breed="persian"/>
</pets>
</animals>
Hope this helped.
Cheers,
Dimitre Novatchev