cascading deletion/copy in xsl

T

tschwartz

I have an xml document in which elements are hierarchically related to
eachother conceptually. Unfortunately, the hierarchical relationship
is not modelled in the schema (i.e., the elements are "flattened" in
the document". I have a case in which I want to remove a high level
element using xslt and want all the related lower-level elements to be
removed as well. Is there an easy way to do this in a template?

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>
 
D

Dimitre Novatchev

I have an xml document in which elements are hierarchically related to
eachother conceptually. Unfortunately, the hierarchical relationship
is not modelled in the schema (i.e., the elements are "flattened" in
the document". I have a case in which I want to remove a high level
element using xslt and want all the related lower-level elements to be
removed as well. Is there an easy way to do this in a template?

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:eek:utput 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:eek:utput 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top