1

I am trying to transform a given XML using xslt. The caveat is that I would have to delete a parent node if a given child node is not present. I did do some template matching, but I am stuck. Any help would be appreciated.

The input xml :

<?xml version="1.0" encoding="UTF-8"?>
<main>
     <item>
        <value>
           <item>
              <value>ABC</value>
              <key>test1</key>
           </item>
           <item>
              <value>XYZ</value>
              <key>test2</key>
           </item>
               <item>
              <value></value>
              <key>test3</key>
           </item>
        </value>
     </item>
     <item>
        <value />
        <key>test4</key>
     </item>
     <item>
        <value>PQR</value>
        <key>test5</key>
     </item>
</main>

Expected Output:

<?xml version="1.0" encoding="UTF-8"?>
<main>
     <item>
        <value>
           <item>
              <value>ABC</value>
              <key>test1</key>
           </item>
           <item>
              <value>XYZ</value>
              <key>test2</key>
           </item>
        </value>
     </item>
     <item>
        <value>PQR</value>
        <key>test5</key>
     </item>
</main>

The issue is if I use template matching e.g.

<xsl:template match="item[not(value)]"/> as mentioned in deleting the parent node if child node is not present in xml using xslt, then it completely removes everything as main/item/value is also empty.

What I need is remove if element is empty but only do if element has no child element.

1
  • Additionally if value for key test1 and test2 is empty then top level item element should also be excluded from output. Commented Jul 20, 2016 at 1:23

3 Answers 3

1

You should first start with the XSLT identity template

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

Then, all you need is a template that matches the item element where all descendent leaf value elements are empty.

 <xsl:template match="item[not(descendant::value[not(*)][normalize-space()])]" />

So, the template matches it, but doesn't output it.

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="item[not(descendant::value[not(*)][normalize-space()])]" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

2 Comments

This works like a charm. How does this xslt differentiate between main/item/value (which is empty and has child node item) against item/value? Is the not(*) doing the trick?
Yes. Doing not(*) checks for a lack of child elements, so it will only match the "leaf" elements.
0

I think you want to remove the element of it has no children at all (whether those children be elements or text nodes). Try inserting this template:

<xsl:template match="item">
    <xsl:if test="exists(value/node())">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:if>
</xsl:template>

3 Comments

I tried it's not working and giving xslt compilation error. Error:XSLTProcessor::transformToXml(): xmlXPathCompiledEval: 1 objects left on the stack.
@Ady211 Are you sure you are using XSLT 2.0, as the tag on your question indicates?
I am using xslt 1.0.
0

If I read this correctly, you want to do:

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="*"/>

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

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

</xsl:stylesheet>

This will remove any item that does not have a value child with some content.

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.