tags:

views:

375

answers:

2

Given an input xml file with following structure:

<root>
  <record row="1" col="1" val="1" />
  <record row="1" col="2" val="2" />
  <record row="1" col="3" val="3" />
  <record row="1" col="n" val="4" />
  <record row="2" col="1" val="5" />
  <record row="2" col="3" val="6" />
  <record row="2" col="n" val="7" />
  <record row="n" col="2" val="8" />
  <record row="n" col="3" val="9" />
  <record row="n" col="n" val="10" />
</root>

How can I output the following structure using XSLT?

<root>
  <row id="1">
    <col id="1">1</col>
    <col id="2">2</col>
    <col id="3">3</col>
    <col id="n">4</col>
  </row>
  <row id="2">
    <col id="1">5</col>
    <col id="2"></col>
    <col id="3">6</col>
    <col id="n">7</col>
  </row>
  <row id="n">
    <col id="1"></col>
    <col id="2">8</col>
    <col id="3">9</col>
    <col id="n">10</col>
  </row>
</root>

[Note how all columns are output even if there is no related element in input]

EDIT: I may have caused confusion through the use of numbers and letters in my example. The solution I am looking for needs to handle row and column attributes that are non-numeric.

+3  A: 
Tomalak
A very nice and complete solution! I don't have anything to add to it. :)
Dimitre Novatchev
Thank you. :-) (I guess I could have used the "iterating without recursion" technique I learned from your answer in the question I linked).
Tomalak
Tomalak - Thank you for the solution; however, I may have caused confusion through the use of numbers and letters in my example. The solution I am looking for needs to handle row and column attributes that are non-numeric.
eft
Have you looked at my answer in the question I linked to? The XSLT there does just that.
Tomalak
I did but am confused by the location path patterns. I am going to post a new question. Thanks.
eft
I accepted this answer as it was very helpful and although it didn't answer my question completely the fault was my wording in the question.
eft
+2  A: 

An XSLT 2.0 solution

This transformation:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    >

    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:variable name="vDoc" as="document-node()"
     select="/"/>

    <xsl:key name="kColsByRow" match="@col" 
         use="../@row"/>

    <xsl:key name="kRecByRowCol" match="record" 
         use="concat(@row,'+',@col)"/>

    <xsl:template match="/*">
      <root>
        <xsl:for-each-group select="*/@row" 
             group-by=".">
          <xsl:sort select="current-grouping-key()"
               data-type="number"/>

           <xsl:variable name="vRow" 
                select="current-grouping-key()"/>  

           <row idd="{$vRow}">

              <xsl:for-each select=
               "1 to max(key('kColsByRow',$vRow)/xs:integer(.))">

                <col idd="{.}">
                  <xsl:value-of select=
                  "key('kRecByRowCol',
                        concat($vRow,'+',.),
                        $vDoc
                       )
                       /
                        @col
                "
                  />
                </col>
              </xsl:for-each>
           </row>    
        </xsl:for-each-group>
      </root>
    </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<root>
    <record row="1" col="1" val="1" />
    <record row="1" col="2" val="2" />
    <record row="1" col="3" val="3" />
    <record row="1" col="10" val="4" />
    <record row="2" col="1" val="5" />
    <record row="2" col="3" val="6" />
    <record row="2" col="10" val="7" />
    <record row="10" col="2" val="8" />
    <record row="10" col="3" val="9" />
    <record row="10" col="10" val="10" />
</root>

produces the wanted result:

<root>
   <row idd="1">
      <col idd="1">1</col>
      <col idd="2">2</col>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
   <row idd="2">
      <col idd="1">1</col>
      <col idd="2"/>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
   <row idd="10">
      <col idd="1"/>
      <col idd="2">2</col>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
</root>
Dimitre Novatchev