1

I am trying to generate a 4 column directory, grouped by the 4 disciplines in my department. One XML file contains the entire group. Each element has a department tag. The construction of the directory would be the following:

Group each entry by discipline.
For each group, cycle through each entry. If the Rank equals Supervisor, fill out the supervisor DIV, otherwise keep generating a div for each person in the group. Once all entries in the group are exhausted, construct the next column for the next group... Keep going until all groups are exhausted.

I'm new to XSLT and really need help. I can create a key for each group, and cycle through the entries in the group, but I'm not sure how to cycle through the different groups.

My mark up is below.

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:key name="group" match="employee" use="dept" />


<xsl:template match="/">

<div id="directory">    
    <div class="man">
        <h1>Manager</h1>
        <h2>John Doe</h2>
    </div>


    <div class="group">
<xsl:for-each select="key('group', 'Mechanical')">
<xsl:sort select="name" order="ascending"/>

                (I need a conditional here to check if rank = supervisor)
        <div class="super">
            <h1>Supervisor</h1>
            <h2><xsl:value-of select="name"/></h2>
        </div>

            <div>
                <p class="name"><xsl:value-of select="name"/></p>
                                    <p class="ID"><xsl:value-of select="id"/></p>

            </div>
  </xsl:for-each>
</div>

XML Directory

<?xml version="1.0" encoding="ISO-8859-1" ?> 

<directory>
  <employee>
    <dept></dept>
    <rank>Manager</rank>
    <name>John Doe</name>
    <id>1234</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>Supervisor</rank>
    <name>Jane Doe</name>
    <id>4321</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee> 
  <employee>
    <dept>Civil</dept>
    <rank>Supervisor</rank>
    <name>Jane Doe</name>
    <id>4321</id>
  </employee>
  <employee>
    <dept>Civil</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
  <employee>
    <dept>Civil</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>Supervisor</rank>
    <name>Jane Doe</name>
    <id>4321</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
 </directory>

Output would look something like: directory

HTML should look something like:

<div id="directory">    
<div class="man">
<h1>Manager</h1>
<h2>John Doe</h2>
</div>
<div class="group">
    <div class="super">
        <h1>Mechanical Supervisor</h1>
        <h2>Supervisor Name</h2>
    </div>
    <div>
        <p class="name">Mech employee name</p>
                    <p class="ID">Mech employee ID</p>
</div><!--end group A-->

<div class="group">
    <div class="super">
        <h1>CivilSupervisor</h1>
        <h2>Supervisor Name</h2>
    </div>
    <div>
        <p class="name">Civil employee name</p>
                    <p class="ID">Civil employee ID</p>
</div><!--end group B-->

<div class="group">
    <div class="super">
        <h1>Electrical Supervisor</h1>
        <h2>Supervisor Name</h2>
    </div>
    <div>
        <p class="name">Electrical employee name</p>
                    <p class="ID">Electrical employee ID</p>
</div><!--end group C-->
</div>
6
  • Please, edit the question and provide the exact wanted output. Also, your XML isn't quite representative -- there is only one department (Mechanical) and one no-name department... Commented Sep 23, 2011 at 21:39
  • Sorry for the crude representation. I'm on my mobile. The no-name department is deliberate, since the manager oversees all the departments. The manager is like a header div. Each disipline is a div that makes up a column. Each individual is in a div according to disipline. Commented Sep 23, 2011 at 22:50
  • Thanks. Still, the wanted output is sketched as plain text, while from your code it is evident you want html output. I can solve the problem of producing the exactly wanted output -- the only problem is that this wanted output isn't yet defined (from the way it looks, it seems to me that the output should be in a table -- however in your code you are generating only divs) -- so, excuse me, but I feel confused. I am not that good in HTML and without knowing the exact wanted output I could generate XML with subtrees for each group and sorted children (superv., members). Commented Sep 23, 2011 at 23:04
  • I appreciate your help. The output will not be in a table, I'm using javascript to insert the result into an html document. I will upload an image of the desired output when get home in a few hours. Thanks again. Commented Sep 24, 2011 at 0:03
  • There.... That's about as descriptive as I can get. :) Commented Sep 24, 2011 at 1:48

1 Answer 1

2

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kEmpByDeptRank" match="employee"
          use="concat(dept,'+', rank)"/>

 <xsl:key name="kEmpByDept" match="employee"
          use="dept"/>

 <xsl:template match="directory">
   <div id="directory">
     <xsl:apply-templates select=
      "key('kEmpByDeptRank', '+Manager')"/>
      <xsl:apply-templates select=
        "employee[not(rank='Manager')]"/>
   </div>
 </xsl:template>

 <xsl:template match=
  "employee[generate-id()
           =
            generate-id(key('kEmpByDept', dept)[1])
           ]
  ">
  <div class="group">
    <xsl:apply-templates mode="inGroup" select=
    "key('kEmpByDeptRank', concat(dept,'+Supervisor'))"/>

    <xsl:apply-templates mode="inGroup" select=
      "key('kEmpByDept', dept)[not(rank='Supervisor')]"/>
  </div>
 </xsl:template>

 <xsl:template match="employee[rank='Manager']">
   <div class="man">
     <h1>Manager</h1>
     <h2><xsl:value-of select="name"/></h2>
   </div>
 </xsl:template>

 <xsl:template match="employee[rank='Supervisor']"
      mode="inGroup">
    <div class="super">
      <h1>
        <xsl:value-of select="concat(dept, ' Supervisor')"/>
      </h1>
      <h2><xsl:value-of select="name"/></h2>
    </div>
 </xsl:template>

 <xsl:template match="employee" mode="inGroup">
   <div>
     <p class="name">
       <xsl:value-of select="concat(dept, ' ', name)"/>
     </p>
     <p class="ID">
       <xsl:value-of select="concat(dept, ' ', id)"/>
     </p>
   </div>
 </xsl:template>
 <xsl:template match="employee"/>
</xsl:stylesheet>

when applied on this XML document (similar to the provided one, but changed the names and Ids to be distinct):

<directory>
  <employee>
    <dept></dept>
    <rank>Manager</rank>
    <name>John Doe</name>
    <id>1234</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>Supervisor</rank>
    <name>Jane Doe</name>
    <id>4321</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>General</rank>
    <name>Joe Doe</name>
    <id>2314</id>
  </employee>
  <employee>
    <dept>Mechanical</dept>
    <rank>General</rank>
    <name>Jim Smith</name>
    <id>2315</id>
  </employee>
  <employee>
    <dept>Civil</dept>
    <rank>Supervisor</rank>
    <name>Ann Smith</name>
    <id>4322</id>
  </employee>
  <employee>
    <dept>Civil</dept>
    <rank>General</rank>
    <name>Peter Pan</name>
    <id>2316</id>
  </employee>
  <employee>
    <dept>Civil</dept>
    <rank>General</rank>
    <name>Mike Sims</name>
    <id>2317</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>Supervisor</rank>
    <name>Amy Dull</name>
    <id>4323</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>General</rank>
    <name>Dan Brown</name>
    <id>2318</id>
  </employee>
  <employee>
    <dept>Electrical</dept>
    <rank>General</rank>
    <name>John Kerry</name>
    <id>2319</id>
  </employee>
 </directory>

produces the wanted, correct result:

<div id="directory">
   <div class="man">
      <h1>Manager</h1>
      <h2>John Doe</h2>
   </div>
   <div class="group">
      <div class="super">
         <h1>Mechanical Supervisor</h1>
         <h2>Jane Doe</h2>
      </div>
      <div>
         <p class="name">Mechanical Joe Doe</p>
         <p class="ID">Mechanical 2314</p>
      </div>
      <div>
         <p class="name">Mechanical Jim Smith</p>
         <p class="ID">Mechanical 2315</p>
      </div>
   </div>
   <div class="group">
      <div class="super">
         <h1>Civil Supervisor</h1>
         <h2>Ann Smith</h2>
      </div>
      <div>
         <p class="name">Civil Peter Pan</p>
         <p class="ID">Civil 2316</p>
      </div>
      <div>
         <p class="name">Civil Mike Sims</p>
         <p class="ID">Civil 2317</p>
      </div>
   </div>
   <div class="group">
      <div class="super">
         <h1>Electrical Supervisor</h1>
         <h2>Amy Dull</h2>
      </div>
      <div>
         <p class="name">Electrical Dan Brown</p>
         <p class="ID">Electrical 2318</p>
      </div>
      <div>
         <p class="name">Electrical John Kerry</p>
         <p class="ID">Electrical 2319</p>
      </div>
   </div>
</div>

and it displays in the browser as:

Manager

John Doe

Mechanical Supervisor

Jane Doe

Mechanical Joe Doe

Mechanical 2314

Mechanical Jim Smith

Mechanical 2315

Civil Supervisor

Ann Smith

Civil Peter Pan

Civil 2316

Civil Mike Sims

Civil 2317

Electrical Supervisor

Amy Dull

Electrical Dan Brown

Electrical 2318

Electrical John Kerry

Electrical 2319

Explanation: Muenchian method for grouping, and locating an employee (Manager or Supervisor) using a composite key (rank, department).

Sign up to request clarification or add additional context in comments.

2 Comments

It works! Thanks! I'm really having trouble getting my head around XSLT transforms. It's like a foreign language. What does the last <xsl:template match="employee"/> accomplish?
@SharpBarb: You are welcome. The purpose of <xsl:template match="employee"/> is to suppress the processing of any employee element by the XSLT built-in template(default XSLT processing). We want to be in full control when and how employee elements are processed. Otherwise, "inGroup" employees will be processed twice -- once by our template in mode "inGroup" and once by the built-in XSLT template -- as result of <xsl:apply-templates select= "employee[not(rank='Manager')]"/>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.