Extreme XSLT 1 Flat to Hierarchial Transform needed

84 views Asked by At

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.

1

There are 1 answers

0
michael.hor257k On BEST ANSWER

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.