pzeszko
pzeszko

Reputation: 2119

Removing empty nodes with empty/no attributes using XSLT

I want to:


EXAMPLE

Before

<a>
  <b>
    <c attr="1"/>
    <d/>
  </b>
</a>

After

<a>
  <b>
    <c attr="1"/>
  </b>
</a>

I have following XSLT:

<xsl:template match="@*|node()">
        <xsl:if test="normalize-space(.) != '' or ./@* != ''">
            <xsl:copy>
                <xsl:copy-of select = "@*[.!='']"/>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:if>
</xsl:template>

It works pretty good, BUT only if my XML has at least one non-empty node! Example:

XML:

<ns1:form xmlns:ns1="http://aaa.com">
  <ns1:test>
    <a></a>
    <b attr1=""/>
    <c attr="123"/>
    <d attr="">
      <e attr="12">NOT EMPTY NODE</e>
    </d>
  </ns1:test>
</ns1:form>

After:

<?xml version="1.0" encoding="UTF-8"?><ns1:form xmlns:ns1="http://aaa.com">
  <ns1:test>
    <c attr="123"/>
    <d>
      <e attr="12">NOT EMPTY NODE</e>
    </d>
  </ns1:test>
</ns1:form>

Works fine, but skipping this non-empty node :

<ns1:form xmlns:ns1="http://aaa.com">
  <ns1:test>
    <a></a>
    <b attr1=""/>
    <c attr="123"/>
    <d attr="">
      <e attr="12"></e>
    </d>
  </ns1:test>
</ns1:form>

the output is:

<?xml version="1.0" encoding="UTF-8"?>

Anyone has any idea why it works like this? My code :

    TransformerFactory factory = TransformerFactory.newInstance();
    InputStream in = new FileInputStream(new File("transform.xslt"));

    Source xslt = new StreamSource(in);
    Transformer transformer = factory.newTransformer(xslt);
    Source text = new StreamSource(new File("input.xml"));

    StringReader reader = new StringReader(xml);
    Source text = new StreamSource(reader);
    transformer.transform(text, new StreamResult(writer));

Cheers

UPDATE

After using your XSLT the output is:

<?xml version="1.0" encoding="UTF-8"?><ns1:form xmlns:ns1="http://aaa.com">
<ns1:test>


<c attr="123"/>
<d>
    <e attr="12">e</e>
</d>
</ns1:test>
</ns1:form>

I wonder why there are empty spaces in my output.xml?


Generally speaking, empty node is a node, which

1) istelf has no attributes or all empty AND

2) has no value f.e. <a></a>, <b/> AND

3) has no children which carry any data (has no children which meet the requirements 1 and 2) Example explaining empty nodes:

<a>
  <b attr="1">
  </b>
  <c>
  <d attr="2">
    <e> value </e>
    <f></f>
  </d>
</a>

a is not empty because it has child b with attribute attr="1" (it's enough, but it also has: x, xx, xxx)

b is not empty because it has non-empty attribute

c is not empty because it has d node with attribute attr="2" (x) and also has child e with some value (xxx)

d is not empty becuase it has non-empty attribute (xx)

e is not empty because it has a value (xxx)

f is empty

Upvotes: 0

Views: 1258

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116959

You should not make <xsl:apply-templates/> conditional - otherwise the stylesheet will stop at the first node that does not satisfy the condition, and it will never get to its children.


BTW, if understand your conditions correctly, you could simplify this to:

<xsl:template match="*[string() or @*[string()]]">
    <xsl:copy>
        <xsl:copy-of select = "@*[string()]"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

Upvotes: 2

Related Questions