Sean2148
Sean2148

Reputation: 365

Removing empty XML elements after creating new elements

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

Answers (1)

zx485
zx485

Reputation: 29022

You can use the following XSLT-1.0 stylesheet. It implements two approaches for solving you problem:

  1. Applying an xsl:if to skip elements you don't want in the output.
  2. Restricting a general element template with a predicate

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

Related Questions