Title

Wednesday, 4 February 2015

XSLT to map multiple source nodes to same target node


We have a collection of orders that come as single XML. One of the lines is mandatory and is called the main line and it is only one per order and is at the header level without any repetition. There are two other line types which are not mandatory but can repeat for each order. Following is the input XML sample and the output is just a collection of lines.

All the lines including main line, all Optional lines and all custom lines need to be mapped to the same element on the target per each order.

 <orderDetails>   <data>   <order>   <poNumber>1234</poNumber>   <storeNumber>35</storeNumber>   <itemNumber>1895550045</itemNumber>   <mainLine>   <referenceNumber>I1</referenceNumber>   <basicInstallCostAmount>100</basicInstallCostAmount>   <basicInstallQuantity>12</basicInstallQuantity>   </mainLine>   <optionalLines>   <optionalLine>   <referenceNumber>OP10</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>15</orderQuantity>   <orderRetailAmount>200</orderRetailAmount>   </optionalLine>   <optionalLine>   <referenceNumber>OP105</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>23</orderQuantity>   <orderRetailAmount>655</orderRetailAmount>   </optionalLine>   </optionalLines>   <customLines>   <customLine>   <referenceNumber>C100</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>123</orderQuantity>   <orderRetailAmount>12300</orderRetailAmount>   </customLine>   <customLine>   <referenceNumber>C1337</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>357</orderQuantity>   <orderRetailAmount>143</orderRetailAmount>   </customLine>   </customLines>   </order>   <order>   <poNumber>5678</poNumber>   <storeNumber>52</storeNumber>   <itemNumber>0005554433</itemNumber>   <mainLine>   <referenceNumber>I21</referenceNumber>   <basicInstallCostAmount>3000</basicInstallCostAmount>   <basicInstallQuantity>35</basicInstallQuantity>   </mainLine>   <optionalLines>   <optionalLine>   <referenceNumber>OP134</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>1500</orderQuantity>   <orderRetailAmount>350000</orderRetailAmount>   </optionalLine>   </optionalLines>   <customLines>   <customLine>   <referenceNumber>C140</referenceNumber>   <duoiCode>EA</duoiCode>   <orderQuantity>13</orderQuantity>   <orderRetailAmount>100</orderRetailAmount>   </customLine>   </customLines>   </order>   </data>  </poWorklistDetailsResponse>

Expected output -

<PoHeadersCollection>   <PoHeaders>   <poNumber>1234</poNumber>   <storeNumber>35</storeNumber>   <itemNumber>1895550045</itemNumber>   <PoLinesCollection>   <PoLines>   <partNumber>I1</partNumber>   <Quantity>12</Quantity>   <installType>Basic</installType>   <lineAmount>100</lineAmount>   </PoLines>   <PoLines>   <partNumber>OP10</partNumber>   <Uom>EA</Uom>   <Quantity>15</Quantity>   <installType>Optional</installType>   <lineAmount>200</lineAmount>   </PoLines>   <PoLines>   <partNumber>OP105</partNumber>   <Uom>EA</Uom>   <Quantity>23</Quantity>   <installType>Optional</installType>   <lineAmount>655</lineAmount>   </PoLines>   <PoLines>   <partNumber>C100</partNumber>   <Uom>EA</Uom>   <Quantity>123</Quantity>   <installType>Custom</installType>   <lineAmount>12300</lineAmount>   </PoLines>   <PoLines>   <partNumber>C1337</partNumber>   <Uom>EA</Uom>   <Quantity>357</Quantity>   <installType>Custom</installType>   <lineAmount>143</lineAmount>   </PoLines>   </PoLinesCollection>   </PoHeaders>   <PoHeaders>   <poNumber>5678</poNumber>   <storeNumber>52</storeNumber>   <itemNumber>0005554433</itemNumber>   <PoLinesCollection>   <PoLines>   <partNumber>I21</partNumber>   <Quantity>35</Quantity>   <installType>Basic</installType>   <lineAmount>3000</lineAmount>   </PoLines>   <PoLines>   <partNumber>OP134</partNumber>   <Uom>EA</Uom>   <Quantity>1500</Quantity>   <installType>Optional</installType>   <lineAmount>350000</lineAmount>   </PoLines>   <PoLines>   <partNumber>C140</partNumber>   <Uom>EA</Uom>   <Quantity>13</Quantity>   <installType>Custom</installType>   <lineAmount>100</lineAmount>   </PoLines>   </PoLinesCollection>   </PoHeaders>  </PoHeadersCollection>

I tried using a for-each and within the loop tried to apply template for the rest of the line types but it is repeating all the nodes.

Below is the XSLT I tried to use and missing on how to apply templates for the current node alone inside the for loop -

<xsl:stylesheet version="1.0">   <xsl:template match="/">   <PoHeadersCollection>   <xsl:for-each select="/orderDetails/data/order">   <ThdIconxPoHeaders>   <poNumber>   <xsl:value-of select="poNumber"/>   </poNumber>   <storeNumber>   <xsl:value-of select="storeNumber"/>   </storeNumber>   <itemNumber>   <xsl:value-of select="itemNumber"/>   </itemNumber>   <PoLinesCollection>   <PoLines>   <partNumber>   <xsl:value-of select="mainLine/referenceNumber"/>   </partNumber>   <Uom>   <xsl:value-of select="mainLine/duoiCode"/>   </Uom>   <Quantity>   <xsl:value-of select="mainLine/basicInstallQuantity"/>   </Quantity>   <installType>   <xsl:text disable-output-escaping="no">Basic</xsl:text>   </installType>   <lineAmount>   <xsl:value-of select="mainLine/basicInstallCostAmount"/>   </lineAmount>   </PoLines>   <xsl:apply-templates select="//*[local-name()='optionalLine']"/>   <xsl:apply-templates select="//*[local-name()='customLine']"/>   </PoLinesCollection>   </PoHeaders>   </xsl:for-each>   </PoHeadersCollection>   </xsl:template>   <xsl:template match="*[local-name()='optionalLine']">   <PoLines>   <partNumber>   <xsl:value-of select="referenceNumber"/>   </partNumber>   <Uom>   <xsl:value-of select="duoiCode"/>   </Uom>   <Quantity>   <xsl:value-of select="orderQuantity"/>   </Quantity>   <installType>   <xsl:text disable-output-escaping="no">Optional</xsl:text>   </installType>   </PoLines>   </xsl:template>   <xsl:template match="*[local-name()='customLine']">   <PoLines>   <partNumber>   <xsl:value-of select="referenceNumber"/>   </partNumber>   <Uom>   <xsl:value-of select="duoiCode"/>   </Uom>   <Quantity>   <xsl:value-of select="orderQuantity"/>   </Quantity>   <installType>   <xsl:text disable-output-escaping="no">Custom</xsl:text>   </installType>   </PoLines>   </xsl:template>  </xsl:stylesheet>

Please Suggest.

Thanks in advance, Geeta

Answer

I would approach it using templates to capture the common structures.

<xsl:template match="/">   <PoHeadersCollection>   <xsl:apply-templates select="orderDetails/data/order" />   </PoHeadersCollection>  </xsl:template>    <xsl:template match="order">   <PoHeaders>   <xsl:copy-of select="poNumber | storeNumber | itemNumber" />   <PoLinesCollection>   <!-- process all the different types of line in one go -->   <xsl:apply-templates select="mainLine   | optionalLines/optionalLine   | customLines/customLine" />   </PoLinesCollection>   </PoHeaders>  </xsl:template>    <xsl:template match="mainLine | optionalLine | customLine">   <PoLines>   <xsl:apply-templates select="referenceNumber"/>   <xsl:apply-templates select="duoiCode"/><!-- won't exist for main line -->   <xsl:apply-templates select="basicInstallQuantity | orderQuantity"/>   <xsl:apply-templates select="." mode="lineType" />   <xsl:apply-templates select="basicInstallCostAmount | orderRetailAmount"/>   </PoLines>  </xsl:template>    <xsl:template match="referenceNumber">   <partNumber><xsl:apply-templates/></partNumber>  </xsl:template>    <xsl:template match="duoiCode">   <Uom><xsl:apply-templates/></Uom>  </xsl:template>    <!-- etc. for other elements -->

and use a special template mode to handle the installType:

<xsl:template match="mainLine" mode="lineType">   <installType>Basic</installType>  </xsl:template>    <xsl:template match="optionalLine" mode="lineType">   <installType>Optional</installType>  </xsl:template>    <xsl:template match="customLine" mode="lineType">   <installType>Custom</installType>  </xsl:template>

No comments:

Post a Comment