Reputation: 37
I have xsl to remove all empty tags, but I only want to remove empty tags if all tags for that section are empty. Take a look at my input file for example:
<NEWORDER>
<ID>1</ID>
<HEADER>
<NMP>NAME PLATE</NMP>
<ORDER>
<USER_ID>USER ID</USER_ID>
<PARTNER_ID>PARTNER ID</PARTNER_ID>
<USER_REFERENCE>5555-55555555-5555</USER_REFERENCE>
<PO_HEADER>
<PO_NUMBER>5555-55555555-5555</PO_NUMBER>
<PO_DATE>20170322</PO_DATE>
<PO_TYPE>BT</PO_TYPE>
<RELEASE_NBR></RELEASE_NBR>
<CUST_ORDER_NBR>5555555555</CUST_ORDER_NBR>
<CONTACT_NAME></CONTACT_NAME>
<CONTACT_PHONE></CONTACT_PHONE>
<TRANS_METHOD></TRANS_METHOD>
<SHIP_COMP>SC</SHIP_COMP>
<CURR_CODE>USD</CURR_CODE>
<INCO_TERMS>FOB</INCO_TERMS>
<NAMED_PLACE></NAMED_PLACE>
<PAYMENT_METHOD>BT</PAYMENT_METHOD>
<TERM_TYPE>555</TERM_TYPE>
<TERM_DESC>NET 10 DAYS</TERM_DESC>
<DEST_BRANCH>5555</DEST_BRANCH>
</PO_HEADER>
<PO_HEADER_NOTES>
<TEXT1_QUAL></TEXT1_QUAL>
<TEXT1_MSG></TEXT1_MSG>
</PO_HEADER_NOTES>
</ORDER>
</HEADER>
</NEWORDER>
In this example I would want the output to only delete the "PO_HEADER_NOTES" section as all inner tags are empty. Example of expected output:
<NEWORDER>
<ID>1</ID>
<HEADER>
<NMP>NAME PLATE</NMP>
<ORDER>
<USER_ID>USER ID</USER_ID>
<PARTNER_ID>PARTNER ID</PARTNER_ID>
<USER_REFERENCE>5555-55555555-5555</USER_REFERENCE>
<PO_HEADER>
<PO_NUMBER>5555-55555555-5555</PO_NUMBER>
<PO_DATE>20170322</PO_DATE>
<PO_TYPE>BT</PO_TYPE>
<RELEASE_NBR></RELEASE_NBR>
<CUST_ORDER_NBR>5555555555</CUST_ORDER_NBR>
<CONTACT_NAME></CONTACT_NAME>
<CONTACT_PHONE></CONTACT_PHONE>
<TRANS_METHOD></TRANS_METHOD>
<SHIP_COMP>SC</SHIP_COMP>
<CURR_CODE>USD</CURR_CODE>
<INCO_TERMS>FOB</INCO_TERMS>
<NAMED_PLACE></NAMED_PLACE>
<PAYMENT_METHOD>BT</PAYMENT_METHOD>
<TERM_TYPE>555</TERM_TYPE>
<TERM_DESC>NET 10 DAYS</TERM_DESC>
<DEST_BRANCH>5555</DEST_BRANCH>
</PO_HEADER>
</ORDER>
</HEADER>
</NEWORDER>
The xsl that I am using to delete all empty nodes is below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:if test=". != '' or ./@* != ''">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@* | node()" />
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="text() | comment() | processing-instruction()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
Please advise. Any help is appreciated.
Upvotes: 0
Views: 706
Reputation: 117165
I only want to remove empty tags if all tags for that section are empty.
That's not a very clear requirement. Looking at the given example, it looks like it should be restated as: remove any element that (a) has children, but (b) does not contain any text nodes - either as direct children, or children of one of its descendants. This would be implemented as:
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="*[* and not(descendant::text())]"/>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<NEWORDER>
<ID>1</ID>
<HEADER>
<NMP>NAME PLATE</NMP>
<ORDER>
<USER_ID>USER ID</USER_ID>
<PARTNER_ID>PARTNER ID</PARTNER_ID>
<USER_REFERENCE>5555-55555555-5555</USER_REFERENCE>
<PO_HEADER>
<PO_NUMBER>5555-55555555-5555</PO_NUMBER>
<PO_DATE>20170322</PO_DATE>
<PO_TYPE>BT</PO_TYPE>
<RELEASE_NBR/>
<CUST_ORDER_NBR>5555555555</CUST_ORDER_NBR>
<CONTACT_NAME/>
<CONTACT_PHONE/>
<TRANS_METHOD/>
<SHIP_COMP>SC</SHIP_COMP>
<CURR_CODE>USD</CURR_CODE>
<INCO_TERMS>FOB</INCO_TERMS>
<NAMED_PLACE/>
<PAYMENT_METHOD>BT</PAYMENT_METHOD>
<TERM_TYPE>555</TERM_TYPE>
<TERM_DESC>NET 10 DAYS</TERM_DESC>
<DEST_BRANCH>5555</DEST_BRANCH>
</PO_HEADER>
</ORDER>
</HEADER>
</NEWORDER>
Upvotes: 1
Reputation: 1190
Given your requirements, the following single template seems to fit the bill:
Note that this is slightly different from your sample XSLT -- for attributes, that only checked if an element's attributes evaluated to ''
, the empty string. In my own experience, I have (rarely) run into cases where an element might have an empty-string attribute, and where that attribute needs to be maintained, so the code below accounts for that by checking for the existence of attributes, rather than just the value of attributes. Tweak to match your requirements.
<xsl:template match="*">
<xsl:choose>
<!-- Strip only if:
1) Element has children.
2) Nothing in the tree starting here contains any text or attributes. -->
<xsl:when test="./* and not(descendant-or-self::*[text() or @*])"/>
<!-- In all other cases, just copy over, and process children. -->
<xsl:otherwise>
<!-- Copies the element itself. -->
<xsl:copy>
<!-- Copies all attributes, if there are any. -->
<xsl:copy-of select="@*"/>
<!-- Sends any children along for further processing. -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Upvotes: 0