I am receiving an XML in the format defined below. I am writing some code to transform the XML in a way that only specific nodes will be flattened out.
<TransactionRequest>
<OrderRequests>
<OrderRequest>
<ReferenceNumber>1234567</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<IsRush>true</IsRush>
<ShippingAddress>
<Name>Tom Hanks</Name>
<Address1>1123 Canada Way</Address1>
<City>Burnaby</City>
<Province>BC</Province>
<PostalCode>V3B13B</PostalCode>
<HomePhone>6048871121</HomePhone>
<BusinessPhone>6041232342</BusinessPhone>
</ShippingAddress>
<ProgramType>C2</ProgramType>
<Comments>Deliver to the receptionist</Comments>
<Items>
<Item>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
</Item>
<Item>
<Sku>UN980011</Sku>
<Quantity>1</Quantity>
</Item>
</Items>
</OrderRequest>
</OrderRequests>
<TraceRequests>
<TraceRequest>
<ReferenceNumber>23432345</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<OriginalReferenceNumber>1234567</OriginalReferenceNumber>
<Comments>Trace Items</Comments>
<TraceItems>
<TraceItem>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
<ShipmentNumber>CM88888990</ShipmentNumber>
</TraceItem>
<TraceItem>
<Sku>UN980011</Sku>
<ProductCode>AA0091</ProductCode>
<Quantity>1</Quantity>
<ShipmentNumber>DDP123123123</ShipmentNumber>
</TraceItem>
</TraceItems>
</TraceRequest>
</TraceRequests>
</TransactionRequest>
I'd like the result to look like the XML that follow. The difference is in this XML the TraceRequest nodes would be split one per each Items/Item node, by duplicating the parent information once for each Items/Item.
<?xml version="1.0"?>
<TransactionRequest>
<OrderRequests>
<OrderRequest>
<ReferenceNumber>1234567</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<IsRush>true</IsRush>
<ShippingAddress>
<Name>Tom Hanks</Name>
<Address1>1123 Canada Way</Address1>
<City>Burnaby</City>
<Province>BC</Province>
<PostalCode>V3B13B</PostalCode>
<HomePhone>6048871121</HomePhone>
<BusinessPhone>6041232342</BusinessPhone>
</ShippingAddress>
<ProgramType>C2</ProgramType>
<Comments>Deliver to the receptionist</Comments>
<Items>
<Item>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
</Item>
<Item>
<Sku>UN980011</Sku>
<Quantity>1</Quantity>
</Item>
</Items>
</OrderRequest>
</OrderRequests>
<TraceRequests>
<TraceRequest>
<ReferenceNumber>23432345</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:开发者_运维百科00-08:00</TransactionTime>
<OriginalReferenceNumber>1234567</OriginalReferenceNumber>
<Comments>Trace Items</Comments>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
<ShipmentNumber>CM88888990</ShipmentNumber>
</TraceRequest>
<TraceRequest>
<ReferenceNumber>23432345</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<OriginalReferenceNumber>1234567</OriginalReferenceNumber>
<Comments>Trace Items</Comments>
<Sku>UN980011</Sku>
<ProductCode>AA0091</ProductCode>
<Quantity>1</Quantity>
<ShipmentNumber>DDP123123123</ShipmentNumber>
</TraceRequest>
</TraceRequests>
</TransactionRequest>
I don't seem to be able to generate an XSLT to flatten only specific sub nodes element of the TraceRequest element.
You can do this by overriding the standard identity transform with extra templates for the special cases.
Firstly, when you match a TraceRequest node, you then want to skip straight to matching TraceItem nodes, where you will do the copying. In this case, the current TraceRequest node can be passed in as a parameter for later copying of its chidlren.
<xsl:template match="TraceRequest">
<xsl:apply-templates select="TraceItems/TraceItem">
<xsl:with-param name="TraceRequestNode" select="."/>
</xsl:apply-templates>
</xsl:template>
Then, when matching the TraceItem you can just copy the children of the TraceRequest passed in via the parameter (excluding the TraceItems node), and also the children of the TraceItem node you are currently positioned on.
<xsl:template match="TraceItem">
<xsl:param name="TraceRequestNode"/>
<TraceRequest>
<xsl:apply-templates select="$TraceRequestNode/*[not(self::TraceItems)]"/>
<xsl:apply-templates select="@*|node()"/>
</TraceRequest>
</xsl:template>
So, given the full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TraceRequest">
<xsl:apply-templates select="TraceItems/TraceItem">
<xsl:with-param name="TraceRequestNode" select="."/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="TraceItem">
<xsl:param name="TraceRequestNode"/>
<TraceRequest>
<xsl:apply-templates select="$TraceRequestNode/*[not(self::TraceItems)]"/>
<xsl:apply-templates select="@*|node()"/>
</TraceRequest>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the output is as follows:
<TransactionRequest>
<OrderRequests>
<OrderRequest>
<ReferenceNumber>1234567</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<IsRush>true</IsRush>
<ShippingAddress>
<Name>Tom Hanks</Name>
<Address1>1123 Canada Way</Address1>
<City>Burnaby</City>
<Province>BC</Province>
<PostalCode>V3B13B</PostalCode>
<HomePhone>6048871121</HomePhone>
<BusinessPhone>6041232342</BusinessPhone>
</ShippingAddress>
<ProgramType>C2</ProgramType>
<Comments>Deliver to the receptionist</Comments>
<Items>
<Item>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
</Item>
<Item>
<Sku>UN980011</Sku>
<Quantity>1</Quantity>
</Item>
</Items>
</OrderRequest>
</OrderRequests>
<TraceRequests>
<TraceRequest>
<ReferenceNumber>23432345</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<OriginalReferenceNumber>1234567</OriginalReferenceNumber>
<Comments>Trace Items</Comments>
<Sku>UN10004</Sku>
<Quantity>2</Quantity>
<ShipmentNumber>CM88888990</ShipmentNumber>
</TraceRequest>
<TraceRequest>
<ReferenceNumber>23432345</ReferenceNumber>
<TransactionTime>2010-11-16T00:00:00-08:00</TransactionTime>
<OriginalReferenceNumber>1234567</OriginalReferenceNumber>
<Comments>Trace Items</Comments>
<Sku>UN980011</Sku>
<ProductCode>AA0091</ProductCode>
<Quantity>1</Quantity>
<ShipmentNumber>DDP123123123</ShipmentNumber>
</TraceRequest>
</TraceRequests>
</TransactionRequest>
Ok, I think I answered my own question bu running the XSLT below:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="TransactionRequest">
<TransactionRequest>
<xsl:copy-of select="OrderRequests"/>
<TraceRequests>
<xsl:for-each select="TraceRequests/TraceRequest/TraceItems/TraceItem">
<TraceRequest>
<xsl:copy-of select="../../ReferenceNumber"/>
<xsl:copy-of select="../../TransactionTime"/>
<xsl:copy-of select="../../OriginalReferenceNumber"/>
<xsl:copy-of select="../../Comments"/>
<xsl:copy-of select="Sku"/>
<xsl:copy-of select="Quantity"/>
<xsl:copy-of select="ShipmentNumber"/>
<xsl:copy-of select="ProductCode"/>
<xsl:copy-of select="DropShipPoNumber"/>
</TraceRequest>
</xsl:for-each>
</TraceRequests>
</TransactionRequest>
</xsl:template>
</xsl:stylesheet>
精彩评论