I have the following simplified XML structure:
<?xml version="1.0" encoding="UTF-8"?>
<ExportData>
<TransportHeader>
<Timestamp>2011-01-16 06:00:33</Timestamp>
<From>
<Name>DynamicExport</Name>
<Version>1.</Version>
</From>
<MessageId>d7b5c5b69a83</MessageId>
</TransportHeader>
<ExportConfig>
<DateTimeFormat>yyyy-MM-dd HH:mm:ss</DateTimeFormat>
<DecimalSymbol>.</DecimalSymbol>
</ExportConfig>
<DataSet>
<Tables>
<Table>
<RH>...</RH>
<Rows>
<R>Data1</R>
<R>Data2</R>
<R>Data3</R>
<R>Data4</R>
<R>Data5</R>
</Rows>
</Table>
</Tables>
</DataSet>
</ExportData>
I have to check if <R> elements exist or not. If no <R> elements exist the mapping has to be aborted, otherwise a <Line> element per <R> needs to be created.
I came up with this solution which works perfectly so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="ISO-8859-1" method="xml" indent="yes" />
<!-- suppress nodes that are not matched -->
<xsl:template match="text() | @*">
<xsl:apply-templates select="text() | @*"/>
</xsl:template>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="not(ExportData/DataSet/Tables/Table/Rows/node())">
<xsl:message terminate="yes">No line items</xsl:message>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/ExportData/DataSet/Tables/Table/Rows">
<INVOIC02>
<!-- apply LINE ITEMS template -->
<xsl:apply-templates select="R"/>
</INVOIC02>
</xsl:template>
<!-- Template creating LINE ITEMS -->
<xsl:template match="R">
<Line>
<elements></elements>
</Line>
</xsl:template>
</xsl:stylesheet>
If there are <R> elements the output is this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<INVOIC02>
<Line>
<elements/>
</Line>
<Line>
<elements/>
</Line>
<Line>
<elements/>
</Line>
<Line>
<elements/>
</Line>
<Line>
<elements/>
</Line>
</INVOIC02>
If there is just <Rows/> and no <R>s the mapping is aborted.
Now I have two questions:
-Is my test for <R> elements robust: test="not(ExportData/DataSet/Tables/Table/Rows/node())" ?
-I am using <xsl:apply-templates> to create the <Line> items instead of an <xsl:for-each> construct. Are my XPath expressions okay or could I make them better?
Well, do you want it to fail if there are no
Relements, or fail ifRowsdoes not have a childnode(), which would include any element (not justR),text(),comment()orprocessing-instruction()?If you really want to verify that there is at least one
Relement that is a child ofRows, you should adjust the test criteria to be more specific:Otherwise, it may pass that test and continue processing and not generate the content you want.
You could get rid of the
<xsl:if>conditional logic inside of your template for the root node and move that logic into a template forRowsthat don’t containRchildren. Putting logic intoxsl:template@matchcriteria makes it easier for XSLT processors to optimize, which can lead to performance gains.