Search code examples
xslthierarchicalflat

Extreme XSLT 1 Flat to Hierarchial Transform needed


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. My actual case is pretty convoluted, but I think I can reduce it down to something like this:

<?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>1</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>2</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>

And it needs to become:

<?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>

And though I know I need to create a number of keys, I can't quite figure out the proper matching to extract and map the data.

I'd really like some help getting started.


Solution

  • I'd really like some help getting started.

    Try this as your starting point:

    <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" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="store" match="tns:storeID" use="." />
    <xsl:key name="order-by-store" match="tns:storeOrder" use="../tns:storeID" />
    <xsl:key name="order-by-id" match="tns:storeOrder" use="." />
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tns:getDataRS">
        <xsl:copy>
            <xsl:copy-of select="tns:Acknowledgement"/>
            <tns:stores>
                <xsl:apply-templates select="tns:customer/tns:storeID[count(. | key('store', .)[1]) = 1]"/>
            </tns:stores>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tns:storeID">
        <tns:store>
            <xsl:copy-of select="."/>       
            <tns:orders>
                <xsl:apply-templates select="key('order-by-store', .)[count(. | key('order-by-id', .)[1]) = 1]"/>
            </tns:orders>
        </tns:store>
    </xsl:template>
    
    <xsl:template match="tns:storeOrder">
        <tns:Order><xsl:value-of select="."/></tns:Order>
        <tns:customers>
            <!-- continue from here... -->
    
        </tns:customers>
    </xsl:template>
    
    
    </xsl:stylesheet>
    

    I am not sure how to continue from this point on, even if I wanted to: I don't see that you have multiple customers per order, and as I said in the comments, the relationship between orders and visits is not quite clear either.