0

I want to:

  • remove all empty nodes with no or all empty attributes f.e. <node/>, <node attr1="" attr2=""/>, <node></node>
  • keep those nodes, which has at least one non-empty attribute f.e. <node attr1="123" attr2="" attr3=""/> and then remove those empty attributes to have <node attr1="123"/>

  • UPDATED keep those empty nodes, which have at least one non-empty child nodes, or which have at least one node with some attributes

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

3
  • Could you clarify what exactly an "empty node" is? It seems like you mean an element with no child text nodes - but it would be best to be sure. Commented May 18, 2015 at 14:51
  • Thanks for quick response! I've updated my question explaining what does empty node exactly mean to me. Commented May 18, 2015 at 15:04
  • Also see my example at the top: I want to keep a,b,c (c - has an attribute; a - has child c which has attribute; b - has child c which has attribute) )node but to remove d (has no children which carry any data). Commented May 18, 2015 at 15:11

1 Answer 1

2

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>
Sign up to request clarification or add additional context in comments.

5 Comments

Unfortunately, If I remove <xsl:apply-tempates/>, no matter If I have one non-empty node or not, the output is always: <?xml version="1.0" encoding="UTF-8"?>
I didn't say you should remove it; on the contrary, you need to have it even if the condition is false.
Thanks Michael - you're great. I guess I need to study XSLT, I'm a newbie at the moment. Two questions: 1) What does exactly string() do? I have trouble finding any reference .. 2) Your XSLT works fine, but it leaves empty spaces and I wonder why? I've updated my question showing example.
1. String: w3.org/TR/xpath-functions/#func-string See also: w3.org/TR/xpath20/#dt-ebv 2. Add <xsl:strip-space elements="*"/> to the top level of your stylesheet.
Hey Michael , it turned out that it works not exactly the way I wanted: completely my fault. I forgot to mention, that I want to keep all those empty nodes, which have any non-empty child or empty-child with any non-empty attribute. I've updated my question at the top, I'd be glad if you could look at it..

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.