user2699474
user2699474

Reputation: 17

Transform flat XML to hierarchical XML

I am trying to create a XSLT to transform the following XML structure

<?xml version="1.0"?>
<rows>
    <row>       
                <e1>A1</e1>
                <l2>B1-01</l2>
                <q2>C1-1-1</q2>
                <n2>D111</n2>
    </row>
    <row>       
                <e1>A1</e1>             
                <l2>B1-01</l2>
                <q2>C1-1-2</q2>
                <n2>D112</n2>
    </row>
    <row>       
                <e1>A1</e1>             
                <l2>B1-02</l2>
                <q2>C1-2-1</q2>
                <n2>D121</n2>
    </row>
    <row>       
                <e1>A1</e1>             
                <l2>B1-02</l2>
                <q2>C1-2-2</q2>
                <n2>D122</n2>
    </row>  
    <row>       
                <e1>A2</e1>             
                <l2>B2-01</l2>
                <q2>C2-1-1</q2>
                <n2>D211</n2>
    </row>
    <row>       
                <e1>A2</e1>             
                <l2>B2-01</l2>
                <q2>C2-1-2</q2>
                <n2>D212</n2>
    </row>
    <row>       
                <e1>A2</e1>             
                <l2>B2-02</l2>
                <q2>C2-2-1</q2>
                <n2>D221</n2>
    </row>
    <row>       
                <e1>A2</e1>             
                <l2>B2-02</l2>
                <q2>C2-2-2</q2>
                <n2>D222</n2>
    </row>      
</rows>

into this

<?xml version="1.0"?>
<e1s xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <e1>A1</e1>
    <l2s>
        <l2>B1-01</l2>
        <q2s>
            <q2>D111</q2>
            <q2>D112</q2>
        </q2s>
        <l2>B1-02</l2>
        <q2s>
            <q2>D121</q2>
            <q2>D122</q2>
        </q2s>
    </l2s>
    <e1>A2</e1>
    <l2s>
        <l2>B2-01</l2>
        <q2s>
            <q2>D211</q2>
            <q2>D212</q2>
        </q2s>
        <l2>B2-02</l2>
        <q2s>
            <q2>D221</q2>
            <q2>D222</q2>
        </q2s>
    </l2s>
</e1s>

I have tried the following XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="no"/>
    <xsl:key name="ke1" match="row" use="e1" />
    <xsl:key name="kl2" match="row" use="concat(e1,'#',l2)" />  
    <xsl:template match="rows"> 
    <e1s>    
    <xsl:for-each select="row[generate-id() = generate-id(key('ke1', e1)[1])]">     
        <e1>
        <xsl:value-of select="e1"/>
        </e1>
        <l2s>       
        <xsl:for-each select="../row[generate-id() = generate-id(key('kl2', concat(e1,'#',l2))[1])]">
            <l2>
            <xsl:value-of select="l2"/>
            </l2>
            <q2s>               
                <xsl:for-each select="key('kl2', concat(e1,'#',l2))">
                    <q2>
                        <xsl:value-of select="n2" />
                    </q2>
                </xsl:for-each>
            </q2s>          
        </xsl:for-each>
        </l2s>      
    </xsl:for-each>
    </e1s>
    </xsl:template>
</xsl:stylesheet>

But the output is not good because it contains "B2"-stuff in the A1 element and vice versa. It seems I don't get the keys right, although I tried for two days now. Can someone please help me with this?

Upvotes: 1

Views: 1100

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122364

Your key definitions are fine, the problem is in your second level of for-each:

<xsl:for-each select="../row[generate-id() = generate-id(key('kl2', concat(e1,'#',l2))[1])]">

Every time around the outer for-each, this select will process all the e1#l2 groups for all values of e1. You need to filter here so that you're only selecting out of the rows that match the current e1, rather than selecting out of all of them:

<xsl:for-each select="key('ke1', e1)[generate-id() = generate-id(key('kl2', concat(e1,'#',l2))[1])]">

With this change I get the correct result.

Upvotes: 1

Related Questions