2

I am trying to recursively remove 'empty' elements (no children, no attribute or empty attribute) from the xml. This is the XSLT I have

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

<xsl:template match="*[not(*) and
                       string-length(.)=0 and
                       (not(@*) or @*[string-length(.)=0])]">
    <xsl:apply-templates/>
</xsl:template>

This is the input XML. I expect this XML to be transformed to a empty string

<world>
    <country>
        <state>
            <city>
                <suburb1></suburb1>
                <suburb2></suburb2>
            </city>
        </state>
    </country>
</world>

But instead I am getting

<world>
    <country>
        <state/>
    </country>
</world>

Can anyone help? I've researched many threads in the forum but still no luck.

2
  • What is your input XML? And probably, whitespace is giving you problems, use normalize-space(). Commented Jul 16, 2014 at 8:59
  • HI Davio, just updated the question with the input XML to make it clearer. Commented Jul 16, 2014 at 10:36

2 Answers 2

7

The condition not(*) is false for any element that has children - regardless of what the children contain.

If you want to "prune" the tree of any branches that do not carry "fruit", try:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*[descendant::text() or descendant-or-self::*/@*[string()]]">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="@*[string()]">
    <xsl:copy/>
</xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

2 Comments

wow! that produces the exact result i need. Should have posted the question earlier saving days of investigation:) If you can do me a favor, can you explain in a bit more detail why it needs the second <template> that match="@*[string()]?
@JamesH It's required to copy (non-empty) attributes to the result tree.
1

So, fighting whitespace, try this:

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

<xsl:template match="*[not(*) and
                       string-length(normalize-space(.))=0 and
                       (not(@*) or @*[string-length(normalize-space(.))=0])]">
    <xsl:apply-templates/>
</xsl:template>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.