Reputation: 13
I'm new to XSLT -- I'm trying to utilize this to recreate (copy) an existing xml document based on a specified attribute.
As an example, with below XML, I want to get the lesson that has the attribute tags="6" from the mainGroup that has the attribute of group="Book1"
<Groups>
<mainGroup id="1" group="Book1">
<subGroup name="Chapter 1">
<lesson name="Lesson1" tags="1" />
<lesson name="Lesson2" tags="2" />
</subGroup>
<subGroup name="Chapter 2">
<lesson name="Lesson1" tags="3" />
<lesson name="Lesson2" tags="4" />
</subGroup>
<subGroup name="Chapter 3">
<subGroup name="Chapter 3 Examples">
<lesson name="Lesson2" tags="5" />
</subGroup>
<lesson name="Lesson1" tags="6" />
</subGroup>
</mainGroup>
<mainGroup id="1" group="Book2">
<subGroup name="Chapter 1">
<lesson name="Lesson1" tags="1" />
<lesson name="Lesson2" tags="2" />
</subGroup>
<subGroup name="Chapter 2">
<lesson name="Lesson1" tags="3" />
<lesson name="Lesson2" tags="4" />
</subGroup>
<subGroup name="Chapter 3">
<subGroup name="Chapter 3 Examples">
<lesson name="Lesson2" tags="6" />
</subGroup>
<lesson name="Lesson1" tags="5" />
</subGroup>
</mainGroup>
</Groups>
with an expected (anticipated) result of the below after XSL is applied
<Groups>
<mainGroup id="1" group="Book1">
<subGroup name="Chapter 3">
<lesson name="Lesson1" tags="6" />
</subGroup>
</mainGroup>
</Groups>
As far as the XSL, what I've come up with so far and the closest I've gotten is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
// Remove all maingroups that are not book1
<xsl:template match="mainGroup[not(@group='Book1')]" />
// Remove all lessons that do not have tags of 5
<xsl:template match="lesson[not(@tags='5')]" />
</xsl:stylesheet>
which gives me the XML result of:
<Groups>
<mainGroup id="1" group="Book1">
<subGroup name="Chapter 1" />
<subGroup name="Chapter 2" />
<subGroup name="Chapter 3">
<subGroup name="Chapter 3 Examples">
<lesson name="Lesson2" tags="5" />
</subGroup>
</subGroup>
</mainGroup>
</Groups>
I cannot figure out how to now remove the empty subGroups --
Some things I've tried (again please don't laugh - new at this XSL :) )
Hoping this removed those that didn't have a lesson node - but didn't seem to do anything
<xsl:template match="subGroup[count(lesson) = 0]" />
<xsl:template match="subGroup[not(node())]" />
and even something like this ---
<xsl:template match="subGroups" mode="copy">
<xsl:choose>
<xsl:when test="count(./*)">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="copy"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
but after hours of no success I'm throwing in the towel - I'm sure someone here probably reading this is thinking -- ahhh, you just need to add or do this -- so any help would be appreciated.
Thanks
Upvotes: 1
Views: 116
Reputation: 116982
Instead of buying everything wholesale, then trying to get rid of the stuff you don't need, why don't you just pick what you do need in the first place?
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Groups">
<xsl:copy>
<xsl:apply-templates select="mainGroup[@group='Book1']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="mainGroup">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="subGroup[lesson[@tags='6']]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="subGroup">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:copy-of select="lesson[@tags='6']"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<Groups>
<xsl:for-each select="Groups/mainGroup[@group='Book1']/subGroup/lesson[@tags='6']">
<mainGroup>
<xsl:copy-of select="../../@*"/>
<subGroup>
<xsl:copy-of select="../@*"/>
<xsl:copy-of select="."/>
</subGroup>
</mainGroup>
</xsl:for-each>
</Groups>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 122364
The key thing is that template rules match against the input XML, so you have to suppress those subGroup
elements that don't contain the lesson you want:
<xsl:template match="subGroup[not(.//lesson/@tags = '5')]"/>
For cases where the rules on which subgroups will ultimately end up empty are more complex it may be easier to do a two stage transformation, with a first pass as you're currently doing and then a second pass that processes the results of the first to strip out the remaining empty tags. In XSLT 2.0 this is straightforward
<xsl:variable name="pass1">
<xsl:apply-templates />
</xsl:variable>
<xsl:apply-templates select="$pass1" mode="pass2"/>
But in 1.0 it requires a node-set
extension function. You can find plenty of examples of this technique in other questions if you want to go that route.
Upvotes: 1