Forum Moderators: open

Message Too Old, No Replies

How do I remove empty attributes

removing all zero length attributes in an xml doc using xsl

         

emsaw

6:05 am on May 10, 2005 (gmt 0)

10+ Year Member



I'm looking for a way to remove all empty/zero length attributes in an XML document using an XSL template declaration.. I just can't figure out how to do it.
For example, I want to remove the 'foo' from the first midNode, and 'bar' from the second midNode.

<topNode>
<midNode foo="" bar="2">
<midNode>
<midNode foo="1" bar="">
</midNode>
</topNode>

I tried this: <xsl:template match="@*[.='']"/>
but that killed the entire output for me, not just empty attributes

Any suggestions?

Thanks

Mark

irnbru

10:20 am on May 10, 2005 (gmt 0)

10+ Year Member



What about matching against "@*[.!='']"

<xsl:for-each select="@*[.!='']">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>

johnl

12:56 pm on May 11, 2005 (gmt 0)

10+ Year Member



Hi,
the following contains some of what I have learnt so far and should not be seen as authoritative:

The template
<xsl:template match="*[@*='']/>
will select any node (with any name) with at least one attribute (with any name) that is a string with zero length. Although within this template, you still have to figure out which of the attributes not to copy (because they are empty) and which to copy.

So it might be better to select all nodes and test all attributes as in the stylesheet below.

This will not select attributes which contain whitespace only. To remove these you could use
<xsl:if test="normalize-space(.)"
which will convert the node to a string and remove leading/trailing whitespace,
instead of
<xsl:if test=".=''">

Furthermore, if all attributes of a certain node are empty, you will end up with an empty node. This might or might not be what you want.

On your road to enlightenment you perhaps will want to try the following stylesheet on any xml:


<?xml version="1.0" encoding="iso8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>

<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="*">
<br/>
<xsl:text>(Start </xsl:text>
<xsl:value-of select='name()'/>
<xsl:text>) </xsl:text>
<xsl:for-each select="@*">
<!-- 'empty' attributes -->
<xsl:if test=".=''">
<xsl:value-of select='name()'/>
<xsl:text>=</xsl:text>
<xsl:text>empty </xsl:text>
</xsl:if>
<!-- 'non-empty' attributes -->
<xsl:if test="not(.='')">
<xsl:value-of select='name()'/>
<xsl:text>=</xsl:text>
<xsl:value-of select='.'/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates/>
<xsl:text> (End </xsl:text>
<xsl:value-of select='name()'/>
<xsl:text>)</xsl:text>
</xsl:template>

</xsl:stylesheet>

emsaw

4:56 am on May 18, 2005 (gmt 0)

10+ Year Member



Thank you both irnbru and johnl.
Johnl, that is a beautiful thing and very helpful. Your xsl really helped me in the right direction.

I employed the following:


<xsl:variable name="Foo"><xsl:value-of select="normalize-space(@c2)"/></xsl:variable>

Then later in my template:


<xsl:if test="string-length($Foo) > 0">
<xsl:attribute name="iFoo"><xsl:value-of select="$Foo"/></xsl:attribute>
</xsl:if>

and it works like a charm. Perhaps there is still a better way? In any case, this is more than adequate.
Thanks again!

Mark