Re: [xsl] How to remove (in addition) empty attributes?

Subject: Re: [xsl] How to remove (in addition) empty attributes?
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 03 Dec 2009 12:50:30 -0500

As so often, the problem here is in the specification.

You have solved the problems of removing elements according to one rule:

1. elements that have no content (child nodes)

and of attributes according to another:

2. attributes that have no value

but not of removing elements defined as "empty" according to a third:

3. elements that contain only elements and attributes that would removed (by any rule).

You do infer that since your third rule is recursive, a full solution will also be recursive.

If you can rewrite rule 3 so it doesn't invoke itself, you can write a transform that doesn't have to process its own results recursively. For example, you might say:

3a. elements none of whose attributes have values and whose children are empty (have no children)

-- but of course 3a will fail if your containment of such elements goes deeper than two.

Here's a recursive solution (note: XSLT 2.0 only). It works by passing the tree through a set of "scrubbing" templates, and also through a set of copying templates, repeating as necessary until both results are the same. It assumes you don't have any whitespace-only text nodes you want to preserve:

<xsl:stylesheet xmlns:xsl="";
  xmlns:xs=""; exclude-result-prefixes="xs"

<xsl:output indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:template match="/|*|@*|text()" mode="copy scrub">
    <xsl:apply-templates select="*|@*|text()" mode="#current"/>

<xsl:template match="*[empty(*|text()|@*[normalize-space()])]" mode="scrub"/>

<xsl:template match="@*[not(normalize-space())]" mode="scrub"/>

<xsl:template match="/">
  <xsl:variable name="scrubbed">
    <xsl:apply-templates select="." mode="scrub"/>
  <xsl:variable name="copied">
    <xsl:apply-templates select="." mode="copy"/>
    <xsl:when test="deep-equal($scrubbed, $copied)">
      <xsl:sequence select="$scrubbed"/>
      <xsl:apply-templates select="$scrubbed"/>


Tested only lightly....


At 02:40 AM 12/3/2009, you wrote:
The problem with this is that only elements OR attributes are stripped but not both at the same time.

Assume the following XML source:

<ns11:elem1 myattr="">
   <ns22:subelem2 />

Then applying the XSLT script below will result in


As you can see the ns11:elem1 persists.
It is handled only in the remove attribute template and does not match the other remove-empty-element template.

So how can I iteratively/recursively re-apply the script/templates on the result again?

XSLT script:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl=""; version="1.0">

<xsl:output indent="yes"/>

<xsl:template match="@*[normalize-space(.)='']"/>

<xsl:template match="*[not(node())]"/>

  <xsl:template match="@* | node()">
       <xsl:apply-templates select="@* | node()"/>


On Wed, 2 Dec 2009 20:58:11 -0500, Syd Bauman wrote:

>How about matching the empty attributes and not generating any

> <xsl:template match="@*[normalize-space(.)='']"/>

>> How can I delete empty attributes as well?

Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.      
17 West Jefferson Street                    Direct Phone: 301/315-9635
Suite 207                                          Phone: 301/315-9631
Rockville, MD  20850                                 Fax: 301/315-8285
  Mulberry Technologies: A Consultancy Specializing in SGML and XML

Current Thread