Reputation: 365
I'm aware there's a lot of examples online of how to remove empty XML elements with XSLT, however I can't find a way that will work with my document. I've created templates that write new elements and would like to process a final template that matches all elements that have no text content inside, this includes elements that are children. If an element contains a child that has no text, I would like only the empty child to be removed. I've tried using identify transforms but they don't seem to work with they way I've setup my templates, but I'd like to keep this structure.
XML
<fruitSet>
<fruit>
<name>apple</name>
<colour>green</colour>
<ratings>
<taste>10</taste>
<look>8</look>
</ratings>
</fruit>
<fruit>
<name>strawberry</name>
<colour>red</colour>
<ratings>
<taste>7</taste>
<look>5</look>
</ratings>
</fruit>
<fruit>
<name>Orange</name>
<colour></colour>
<ratings>
<taste>3</taste>
<look></look>
</ratings>
</fruit>
</fruitSet>
XSL
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<RDF>
<xsl:apply-templates select = "fruitSet/fruit"/>
<xsl:apply-templates select = "fruitSet/fruit/ratings"/>
</RDF>
</xsl:template>
<xsl:template match = "fruit">
<Description>
<xsl:attribute name = "ID">
<xsl:value-of select = "name/text()"/>
</xsl:attribute>
<hasColour><xsl:value-of select = "colour/text()"/></hasColour>
<hasTasteRating>
<xsl:apply-templates select = "ratings"/>
</hasTasteRating>
</Description>
</xsl:template>
<xsl:template match = "ratings">
<xsl:value-of select = "taste/text()"/>
</xsl:template>
</xsl:stylesheet>
Desired Output
<?xml version="1.0" encoding="utf-16"?>
<RDF xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<Description ID="apple">
<hasColour>green</hasColour>
<hasTasteRating>10</hasTasteRating>
</Description>
<Description ID="strawberry">
<hasColour>red</hasColour>
<hasTasteRating>7</hasTasteRating>
</Description>
<Description ID="Orange">
<hasTasteRating>3</hasTasteRating>
</Description>1073</RDF>
</RDF>
Upvotes: 0
Views: 50
Reputation: 29022
You can use the following XSLT-1.0 stylesheet. It implements two approaches for solving you problem:
xsl:if
to skip elements you don't want in the output.This is the whole stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<RDF>
<xsl:apply-templates select = "fruitSet/fruit"/>
</RDF>
</xsl:template>
<xsl:template match = "fruit">
<Description>
<xsl:attribute name = "ID">
<xsl:value-of select = "name/text()"/>
</xsl:attribute>
<xsl:if test="normalize-space(colour)">
<hasColour>
<xsl:value-of select = "colour"/>
</hasColour>
</xsl:if>
<xsl:apply-templates select="ratings" />
</Description>
</xsl:template>
<!-- Only create a <hasTasteRating> element for all elements with a present <taste> child -->
<xsl:template match="*[normalize-space(taste)]" >
<hasTasteRating>
<xsl:value-of select="taste/text()"/>
</hasTasteRating>
</xsl:template>
</xsl:stylesheet>
Its output is:
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<Description ID="apple">
<hasColour>green</hasColour>
<hasTasteRating>10</hasTasteRating>
</Description>
<Description ID="strawberry">
<hasColour>red</hasColour>
<hasTasteRating>7</hasTasteRating>
</Description>
<Description ID="Orange">
<hasTasteRating>3</hasTasteRating>
</Description>
</RDF>
Upvotes: 1