XQuery/Displaying data in HTML Tables

Motivation edit

You would like to display your XML data in an HTML tables and display alternate rows using a colored background.

Data File edit

Assume you have a data file such as the following XML file which is a sample glossary of terms and definitions:

terms.xml edit

<terms>
   <term>
      <term-name>Object Class</term-name>
      <definition>A set of ideas, abstractions, or things in the real world
      that are identified with explicit boundaries and meaning and whose properties
      and behavior follow the same rules</definition>
   </term>
   <term>
      <term-name>Organization</term-name>
      <definition>A unit consisting of people and processes established
      to perform some functions</definition>
       </term>
</terms>

The <term> tags will repeat for each term in your glossary.

You would like to display these terms in an HTML table.

Screen Image edit

 
HTML table screen image

The following XQuery will perform the task.

Sample Code edit

xquery version "1.0";
declare option exist:serialize "method=xhtml media-type=text/html";

let $my-doc := doc('terms.xml')
return
<html>
    <head>
        <title>Terms</title>
    </head>
    <body>
    <table border="1">
    <thead>
      <tr>
          <th>Term</th> 
          <th>Definition</th>
      </tr>
    </thead>
    <tbody>{
       for $term at $count in $my-doc/terms/term
            let $term-name := $term/term-name/text()
            order by upper-case($term-name)
       return
         <tr> {if ($count mod 2) then (attribute bgcolor {'Lavender'}) else ()}
           <td>{$term/term-name/text()}</td>
           <td>{$term/definition/text()}</td>
         </tr>
       }</tbody>
     </table>
   </body>
</html>

Discussion edit

Sorting before counting edit

There are two nested for loops. The outer loop has the additional at count parameter that increments a counter for each result returned. The inner loop has the loop that returns a generic sorted item to the outer loop. Note that the inner loop does the sorting first and the outer loop does the counting of each item so that alternate rows are shaded.

Note that if you know the original file is in the correct order the nested for loops are not necessary. A single for loop with the at $count is all that is needed.

Dynamic Element Construction edit

The following lines:

<tr> {if ($count mod 2)
         then (attribute bgcolor {'Lavender'})
         else ()}

conditionally creates a light blue background color for odds rows, rows which evaluate true because modulus 2 of their $count is not zero. This is an example of dynamic element construction.

Odd rows:

<tr bgcolor="Lavender">
   <td>...</td>
</tr>

Even rows:

<tr><td>...</td></tr>

It does this by conditionally adding an attribute bgcolor="Lavender" for odd rows in the table. If the test ($count mod 2) is zero, i.e. on even rows, an attribute will not be added.

It is recommended best practice that the style of shading alternate rows of a table be done in a central cascading style sheet. The most general way to keep the table formats standard throughout your site would be to add semantic class tags to each row to label them even or odd.

<tr> {if ($count mod 2)
         then (attribute  class  {'even'})
         else (attribute  class  {'odd'})}

The CSS file would then contain the following:

.odd {background-color: Lavender;}