tags:

views:

31

answers:

3

Using this input XML:

<?xml version="1.0" encoding="utf-8"?>
<Employees>
   <Employee ID="1">
      <FirstName>Klaus</FirstName>
      <LastName>Salchner</LastName>
   </Employee>
   <Employee ID="2">
      <FirstName>Peter</FirstName>
      <LastName>Pan</LastName>
   </Employee>
</Employees>

How would you get this output:

<Employees>
    <FirstName>
        <Employee>Klaus</Employee>
        <Employee>Peter</Employee>
    </FirstName>
    <LastName>
        <Employee>Salchner</Employee>
        <Employee>Pan</Employee>
    </LastName>
</Employees>

BUT- If you don't know how many fields there are going to be in the Employee elements - however, lets assume that the same elements (here being, FirstName and LastnName) will definately be present in every Employee element.

The best I've got is:

<Employees>
    <xsl:for-each select="*/Employee/.">
        <xsl:value-of select=".">
            <xsl:value-of select="./." />
        </xsl:value-of>
    </xsl:for-each>
</Employees>

And I know that's wrong!

Any and all help greatly appreciated!

Thanks,

Matt.

A: 

If you need to process nodes more than once you can make use of template modes. The following would do:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Employees">
    <Employees>
      <FirstName>
        <xsl:apply-templates mode="firstname" />
      </FirstName>
      <LastName>
        <xsl:apply-templates mode="lastname" />
      </LastName>
    </Employees>
  </xsl:template>

  <xsl:template match="Employee" mode="firstname">
    <Employee>
      <xsl:value-of select="FirstName"/>
    </Employee>
  </xsl:template>

  <xsl:template match="Employee" mode="lastname">
    <Employee>
      <xsl:value-of select="LastName"/>
    </Employee>
  </xsl:template>

</xsl:stylesheet>
0xA3
Thanks :) The only problem with this is that I don't know what the firstname and lastname nodes are going to be called. Argh!
Matt W
A: 

Well, I worked it out in the end - basically I need a for-each for the elements in the first Employee and assign a variable inside the for-each to the position() value.

Then, in a second, nested for-each, I loop through the outer Employee elements.

For each Employee element I use the variable (which contains the "row" of the inner element) to index it's inner element.

Something like:

<xsl:for-each select="*/Employee[1]/.">
    <tr>
        <xsl:variable name="row" select="position()" />
        <xsl:for-each select="/*/Employee">
            <td>
                <xsl:value-of select=".[$row]/."/>
            </td>
        </xsl:for-each>
    </tr>
</xsl:for-each>

I will admit, mine is a little more concise, but that's the gist.

In short (deep breath) loop through the list of elements of the first outer element. For each one, loop through the outer elements and use the index of the inner elements to pick the inner elements sequentially.

Matt W
+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kElsByName" match="Employee/*"
  use="name()"/>

 <xsl:template match="/*">
   <Employees>
     <xsl:for-each select=
      "Employee/*[generate-id()
                 =
                  generate-id(key('kElsByName', name())[1])
                  ]">
       <xsl:element name="{name()}">
         <xsl:for-each select="key('kElsByName', name())">
           <Employee>
             <xsl:value-of select="."/>
           </Employee>
         </xsl:for-each>
       </xsl:element>
     </xsl:for-each>
   </Employees>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document (added <DOB> to make it generic):

<Employees>
   <Employee ID="1">
      <FirstName>Klaus</FirstName>
      <LastName>Salchner</LastName>
      <DOB>19670823</DOB>
   </Employee>
   <Employee ID="2">
      <FirstName>Peter</FirstName>
      <LastName>Pan</LastName>
      <DOB>19881113</DOB>
   </Employee>
</Employees>

produces the wanted result:

<Employees>
   <FirstName>
      <Employee>Klaus</Employee>
      <Employee>Peter</Employee>
   </FirstName>
   <LastName>
      <Employee>Salchner</Employee>
      <Employee>Pan</Employee>
   </LastName>
   <DOB>
      <Employee>19670823</Employee>
      <Employee>19881113</Employee>
   </DOB>
</Employees>

Do note:

  1. The use of keys and the use of the Muenchian method for grouping in order to find all different names of elements that are children of Employee.

  2. The use of <xsl:element> with an AVT for its name attribute to generate elements with unknown at compile time name.

Dimitre Novatchev