I have a rather complicated xslt sheet transforming one xml format to another using templates. However, in the resulting xml, I need to have all the empty elements excluded. How is that done?
This is how the base xslt looks like:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:far="http://www.itella.com/fargo/fargogate/" xmlns:a="http://tempuri.org/XMLSchema.xsd" xmlns:p="http://tempuri.org/XMLSchema.xsd">
<xsl:import href="TransportCDMtoFDM_V0.6.xsl"/>
<xsl:import href="ConsignmentCDMtoFDM_V0.6.xsl"/>
<xsl:template match="/">
<InboundFargoMessage>
<EdiSender>
<xsl:value-of select="TransportInformationMessage/SenderId"/>
</EdiSender>
<EdiReceiver>
<xsl:value-of select="TransportInformationMessage/RecipientId"/>
</EdiReceiver>
<EdiSource>
<xsl:value-of select="TransportInformationMessage/Waybill/Parties/Consignor/Id"/>
</EdiSource>
<EdiDestination>FARGO</EdiDestination>
<Transportations>
<xsl:for-each select="TransportInformationMessage/TransportUnits/TransportUnit">
<xsl:call-template name="transport"/>
</xsl:for-each>
<xsl:for-each select="TransportInformationMessage/Waybill/TransportUnits/TransportUnit">
<xsl:call-template name="transport"/>
</xsl:for-each>
<xsl:for-each select="TransportInformationMessage/Waybill">
<EdiImportTransportationDTO>
<Consignments>
<xsl:for-each select="Shipments/Shipment">
<xsl:call-template name="consignment"/>
</xsl:for-each>
</Consignments>
<EdiTerminalDepartureTime>
<xsl:value-of select="DatesAndTimes/EstimatedDepartureDateTime"/>
<xsl:value-of select="DatesAndTimes/DepartureDateTime"/>
</EdiTerminalDepartureTime>
<EdiAgentTerminalArrivalDate>
<xsl:value-of select="DatesAndTimes/EstimatedArrivalDateTime"/>
<xsl:value-of select="DatesAndTimes/ArrivalDateTime"/>
</EdiAgentTerminalArrivalDate>
<EdiActivevehicle>
<xsl:value-of select="Vehicle/TransportShiftNumber"/>
</EdiActivevehicle>
<EdiConveyerZipCodeTown><xsl:text> </xsl:text></EdiConveyerZipCodeTown>
</EdiImportTransportationDTO>
</xsl:for-each>
</Transportations>
</InboundFargoMessage>
</xsl:template>
</xsl:stylesheet>
What needs to be added, so that empty elements are left out?
For example, a snippet from the resulting xml:
<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
xmlns:far="http://www.itella.com/fargo/fargogate/"
xmlns:a="http://tempuri.org/XMLSchema.xsd">
<EdiSender>XXXX</EdiSender>
<EdiReceiver>YYYY</EdiReceiver>
<EdiSource>TR/BAL/IST</EdiSource>
<EdiDestination>FARGO</EdiDestination>
<Transportations>
<EdiImportTransportationDTO>
<Consignments>
<EdiImportConsignmentDTO>
<ConsignmentLines>
<EdiImportConsignmentLineDTO>
<DangerousGoodsItems>
<EdiImportDangerGoodsItemDTO>
<EdiKolliTypeOuter/>
<EdiKolliTypeInner/>
<EdiTechnicalDescription/>
<EdiUNno/>
<EdiClass/>
<EdiDangerFactor/>
<EdiEmergencyTemperature/>
</EdiImportDangerGoodsItemDTO>
</DangerousGoodsItems>
<BarCodes>
<EdiImportConsignmentLineBarcodeDTO/>
</BarCodes>
<EdiNumberOfPieces>00000002</EdiNumberOfPieces>
<EdiGrossWeight>0.000</EdiGrossWeight>
<EdiHeight/>
<EdiWidth/>
<EdiLength/>
<EdiGoodsDescription/>
<EdiMarkingAndNumber/>
<EdiKolliType>road</EdiKolliType>
<EdiCbm/>
<EdiLdm/>
</EdiImportConsignmentLineDTO>
That really needs to be:
<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
xmlns:far="http://www.itella.com/fargo/fargogate/"
xmlns:a="http://tempuri.org/XMLSchema.xsd">
<EdiSender>XXXX</EdiSender>
<EdiReceiver>YYYY</EdiReceiver>
<EdiSource>TR/BAL/IST</EdiSource>
<EdiDestination>FARGO</EdiDestination>
<Transportations>
<EdiImportTransportationDTO>
<Consignments>
<EdiImportConsignmentDTO>
<ConsignmentLines>
<EdiImportConsignmentLineDTO>
<DangerousGoodsItems/>
<BarCodes/>
<EdiNumberOfPieces>00000002</EdiNumberOfPieces>
<EdiGrossWeight>0.000</EdiGrossWeight>
<EdiKolliType>road</EdiKolliType>
</EdiImportConsignmentLineDTO>
In other words: Empty elements should be left out.
The provided (partial) XSLT code illustrates well an XSLT antipattern. Try almost always to avoid the use of <xsl:for-each>
.
Below there is a sample XML document and a transformation which copies all nodes with the exception of the "empty" elements. Here by "empty" we mean either childless, or with one child whitespace-only child node.
XML Document:
<a>
<b>
<c> </c>
<d/>
<e>1</e>
</b>
</a>
Transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[not(node())]
|
*[not(node()[2])
and
node()/self::text()
and
not(normalize-space())
]
"/>
</xsl:stylesheet>
Result:
<a>
<b>
<e>1</e>
</b>
</a>
Do note:
The use of the Identity Rule.
How we override the Identity Rule with a template that only matches "empty" elements. As this template does nothing (has no body at all), this doesn't copy ("deletes") the "empty" elements.
Using and overriding the Identity Rule is the most important XSLT design pattern.
This is probably the simplest way:
<xsl:for-each select="Nodes/Node[text() != '']">
</xsl:for-each>
If you have control of the XML generation then don't add the root node if there is no children. Regardless of which way you choose XSL is quite verbose.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With