I am fighting a pretty extreme case of transforming a flat XML into a hierarchical one. I'm also stuck with using XSLT 1.0. It is acting like it wants to do what I'm trying, but it is not quite there. You'll likely have to run it to see. Clearly I need to get my head around how these keys work. Almost all the examples I find are based upon the value of an attribute... and not a cross-reference between different nodes.
Here is my input:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<tns:getDataRS xmlns:tns="http://www.myco.com/DataService">
<tns:Acknowledgement>Process completed successfully.</tns:Acknowledgement>
<tns:customer>
<tns:customerID>210</tns:customerID>
<tns:visitID>12</tns:visitID>
<tns:storeID>1</tns:storeID>
<tns:storeOrder>28</tns:storeOrder>
<tns:itemID>1</tns:itemID>
<tns:customerSalesDate>2014-09-26</tns:customerSalesDate>
</tns:customer>
<tns:customer>
<tns:customerID>210</tns:customerID>
<tns:visitID>12</tns:visitID>
<tns:storeID>1</tns:storeID>
<tns:storeOrder>28</tns:storeOrder>
<tns:itemID>3</tns:itemID>
<tns:customerSalesDate>2014-09-26</tns:customerSalesDate>
</tns:customer>
<tns:customer>
<tns:customerID>211</tns:customerID>
<tns:visitID>31</tns:visitID>
<tns:storeID>2</tns:storeID>
<tns:storeOrder>48</tns:storeOrder>
<tns:itemID>2</tns:itemID>
<tns:customerSalesDate>2014-09-26</tns:customerSalesDate>
</tns:customer>
<tns:customer>
<tns:customerID>211</tns:customerID>
<tns:visitID>31</tns:visitID>
<tns:storeID>2</tns:storeID>
<tns:storeOrder>48</tns:storeOrder>
<tns:itemID>4</tns:itemID>
<tns:customerSalesDate>2014-09-26</tns:customerSalesDate>
</tns:customer>
<tns:item>
<tns:customerID>210</tns:customerID>
<tns:visitID>12</tns:visitID>
<tns:storeID>1</tns:storeID>
<tns:itemID>1</tns:itemID>
<tns:unitPrice>2.95</tns:unitPrice>
<tns:quantity>4</tns:quantity>
</tns:item>
<tns:item>
<tns:customerID>211</tns:customerID>
<tns:visitID>31</tns:visitID>
<tns:storeID>2</tns:storeID>
<tns:itemID>2</tns:itemID>
<tns:unitPrice>3.29</tns:unitPrice>
<tns:quantity>2</tns:quantity>
</tns:item>
<tns:item>
<tns:customerID>210</tns:customerID>
<tns:visitID>12</tns:visitID>
<tns:storeID>1</tns:storeID>
<tns:itemID>3</tns:itemID>
<tns:unitPrice>4.99</tns:unitPrice>
<tns:quantity>1</tns:quantity>
</tns:item>
<tns:item>
<tns:customerID>211</tns:customerID>
<tns:visitID>31</tns:visitID>
<tns:storeID>2</tns:storeID>
<tns:itemID>4</tns:itemID>
<tns:unitPrice>6.95</tns:unitPrice>
<tns:quantity>2</tns:quantity>
</tns:item>
</tns:getDataRS>
</env:Body>
</env:Envelope>
Here is my XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tns="http://www.myco.com/DataService">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="storeKey" match="*[local-name() = 'storeID']" use="."/>
<xsl:key name="orderKey" match="*[local-name() = 'storeOrder']" use="."/>
<xsl:key name="customerKey" match="*[local-name() = 'customerID']" use="."/>
<xsl:key name="itemKey" match="*[local-name() = 'itemID']" use="."/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[local-name() = 'getDataRS']">
<xsl:copy>
<tns:stores>
<xsl:for-each select="//*[local-name() = 'storeID'][count(. | key('storeKey', .)) > 0]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</tns:stores>
</xsl:copy>
</xsl:template>
<xsl:template match="*[local-name() = 'storeID']">
<tns:store>
<xsl:copy-of select="."/>
<tns:orders>
<xsl:for-each select="../*[local-name() = 'storeOrder'][count(. | key('orderKey', .)) > 0]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</tns:orders>
</tns:store>
</xsl:template>
<xsl:template match="*[local-name() = 'storeOrder']">
<tns:order><xsl:value-of select="."/></tns:order>
<tns:salesDate><xsl:value-of select="../*[local-name() = 'customerSalesDate']"/></tns:salesDate>
<tns:customers>
<xsl:for-each select="../*[local-name() = 'customerID'][count(. | key('customerKey', .)) > 0]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</tns:customers>
</xsl:template>
<xsl:template match="*[local-name() = 'customerID']">
<tns:customer>
<xsl:copy-of select="."/>
<xsl:copy-of select="../*[local-name() = 'visitID']"/>
<tns:items>
<xsl:for-each select="../*[local-name() = 'itemID'][count(. | key('itemKey', .)) > 0]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</tns:items>
</tns:customer>
</xsl:template>
<xsl:template match="*[local-name() = 'itemID']">
<tns:item>
<xsl:copy-of select="."/>
<xsl:copy-of select="../*[local-name() = 'unitPrice']"/>
<xsl:copy-of select="../*[local-name() = 'quantity']"/>
</tns:item>
</xsl:template>
</xsl:stylesheet>
Here is my desired output:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<tns:getDataRS xmlns:tns="http://www.myco.com/DataService">
<tns:Acknowledgement>Process completed successfully.</tns:Acknowledgement>
<tns:stores>
<tns:store>
<tns:storeID>1</tns:storeID>
<tns:orders>
<tns:order>28</tns:order>
<tns:salesDate>2014-09-26</tns:salesDate>
<tns:customers>
<tns:customer>
<tns:customerID>210</tns:customerID>
<tns:visitID>12</tns:visitID>
<tns:items>
<tns:item>
<tns:itemID>1</tns:itemID>
<tns:unitPrice>2.95</tns:unitPrice>
<tns:quantity>4</tns:quantity>
</tns:item>
<tns:item>
<tns:itemID>3</tns:itemID>
<tns:unitPrice>4.99</tns:unitPrice>
<tns:quantity>1</tns:quantity>
</tns:item>
</tns:items>
</tns:customer>
</tns:customers>
</tns:orders>
</tns:store>
<tns:store>
<tns:storeID>2</tns:storeID>
<tns:orders>
<tns:order>48</tns:order>
<tns:salesDate>2014-09-26</tns:salesDate>
<tns:customers>
<tns:customer>
<tns:customerID>211</tns:customerID>
<tns:visitID>31</tns:visitID>
<tns:items>
<tns:item>
<tns:itemID>2</tns:itemID>
<tns:unitPrice>3.29</tns:unitPrice>
<tns:quantity>2</tns:quantity>
</tns:item>
<tns:item>
<tns:itemID>4</tns:itemID>
<tns:unitPrice>6.95</tns:unitPrice>
<tns:quantity>2</tns:quantity>
</tns:item>
</tns:items>
</tns:customer>
</tns:customers>
</tns:orders>
</tns:store>
</tns:stores>
</tns:getDataRS>
</env:Body>
</env:Envelope>