Dealing with Attributes and Elements in XPath with XSLT using ASP.NET 2.0

This is the second article in a series focusing on XPath with XSLT using ASP.NET 2.0. The first article in this series focused on working elements. In this article, I will be focusing on working with attributes, attributes together with elements and searching for elements at various positions.


A downloadable zip file is available for this article.

Even though this series is based on ASP.NET 2.0, I use it only as a transformation engine. The entire focus will be on working with XPath and XSLT. You can use any transformation engine according to your requirements, such as Java or others.

If you are new to a few of XSLT’s concepts, I strongly suggest that you go through my first series, “Developing XSLT based Applications using ASP.NET 2.0.” It deals completely with XSLT.  I will be using a few of the XSLT constructs in this series wherever appropriate. The entire setup for this article (along with ASP.NET 2.0 code and sample XML code) was listed in the first article of this series.

The entire solution (source code) for this article is available as a free download (in the form of a zip). All the applications in this series have been developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Standard Edition together with Microsoft SQL Server 2005 Developer Edition as the database. You can even directly copy and paste the code given in this article and save it with the extensions XML or XSL to work with your own XSL parsers/engines.

I didn’t really test any of the code in any of the other tools/IDEs/servers/editions/versions. If you have any problems, please feel free to post in the discussion area.

Retrieving all values of a particular attribute at a particular hierarchy (or path)

In the previous article, we focused on working with only elements. Now it is time to work on attributes. Let us consider that I would like to list all the department names. All department names are available in the “Dname” attribute of the  “Department” tag. The path of the “Department” tag is just below the root tag “EmployeeeDetails.”

The following is the first approach we can use to retrieve all values of a particular attribute at a particular location (using a separate template):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:apply-templates select ="EmployeeDetails/Department"/>

            </b>

      </xsl:template>

      <xsl:template match="EmployeeDetails/Department">

            <xsl:value-of select ="@Dname"/>

            <br/>

      </xsl:template>

</xsl:stylesheet>

Within the above code, you can observe that I am using “@Dname,” which stands for “attribute Dname.”  The following is the second approach we can use to accomplish our goal (using the “xsl:for-each” construct).

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:for-each select="EmployeeDetails/Department">

                        <xsl:value-of select="@Dname"/>

                        <br/>

                  </xsl:for-each>

            </b>

      </xsl:template>

</xsl:stylesheet>

This third approach is also valid:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:apply-templates select ="EmployeeDetails/Department/@Dname"/>

            </b>

      </xsl:template>

      <xsl:template match="EmployeeDetails/Department/@Dname">

            <xsl:value-of select ="."/>

            <br/>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the XSLT gets executed, the transformation looks something like the following:

<b>

      Accounting<br />

      Sales<br />

      Research<br />

</b>

{mospagebreak title=Retrieving all values of a particular attribute at any level of hierarchy (or path)}

In the previous section, we tried to search for an attribute at a particular level of the hierarchy (or path).  Now I would like to retrieve the values of a particular attribute regardless the hierarchy of its element.

The following is the first approach we can use to retrieve all the values of a particular attribute at any location (using a separate template):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:apply-templates select ="//@Dname"/>

            </b>

      </xsl:template>

      <xsl:template match="@Dname">

            <xsl:value-of select ="."/>

            <br/>

      </xsl:template>

</xsl:stylesheet>

Within the above code, you can observe that I am using “//@Dname” which stands for “anywhere the attribute Dname exists.”  The following is the second approach we can use to accomplish our goal (using the “xsl:for-each” construct).

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:for-each select="//@Dname">

                        <xsl:value-of select="."/>

                        <br/>

                  </xsl:for-each>

            </b>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the XSLT gets executed, the transformation would be the same as provided in previous section

{mospagebreak title=Retrieving all values of all attributes available at any level of hierarchy (or path)}

In the previous sections, we tried to search for a particular attribute. Now I would like to retrieve the values of all attributes present in the document, regardless of what element or hierarchy it belongs to!

The following is the first approach we can use to retrieve all values of any attribute at any location (using a separate template):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:apply-templates select ="//@*"/>

            </b>

      </xsl:template>

      <xsl:template match="@*">

            <xsl:value-of select ="."/>

            <br/>

      </xsl:template>

</xsl:stylesheet>

Within the above code, you can observe that I am using “//@*” which stands for “any attribute at any path.” The following is the second approach we can use to accomplish our goal (using the “xsl:for-each” construct).

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <b>

                  <xsl:for-each select="//@*">

                        <xsl:value-of select="."/>

                        <br/>

                  </xsl:for-each>

            </b>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the XSLT gets executed, the transformation looks something like the following:

<b>

      10<br />

      Accounting<br />

      New York<br />

      20<br />

      Sales<br />

      Dallas<br />

      30<br />

      Research<br />

      Washington<br />

</b>

{mospagebreak title=Retrieving values of attributes and elements simultaneously}

Now that we understand how to retrieve the values of both elements and attributes, it is time to retrieve all the values and display (transform) them in a meaningful manner.

I would like to display all the details for employees by department.  The following is the first approach we can use to accomplish this task (using separate template):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

                  <xsl:apply-templates select ="EmployeeDetails/Department"/>

      </xsl:template>

      <xsl:template match="EmployeeDetails/Department">

            <b>Department: <xsl:value-of select ="@Dname"/></b><hr />

            <xsl:apply-templates select ="Employee"/>

            <br/>

      </xsl:template>

      <xsl:template match ="Employee"     >

            <xsl:value-of select ="Empno"/>,

            <xsl:value-of select ="Ename"/>,

            <xsl:value-of select ="Sal"/><br />

      </xsl:template>

     

</xsl:stylesheet>

From the above, you can understand that I am using two separate templates to transform in a meaningful manner. The first template deals with the “Department.”  In this template, I am trying to retrieve the value of the attribute “Dname.”  The second template deals with the “Employee.” This deals with all the elements available under “Employee.” The following is the second approach we can use to accomplish the same task (using a nested “xsl:for-each” construct).

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

                  <xsl:for-each select="EmployeeDetails/Department">

                        <b>

                              Department: <xsl:value-of select ="@Dname"/>

                        </b>

                        <hr />

                        <xsl:for-each select="Employee">

                              <xsl:value-of select ="Empno"/>,

                              <xsl:value-of select ="Ename"/>,

                              <xsl:value-of select ="Sal"/><br />

                        </xsl:for-each>

                        <br />

                  </xsl:for-each>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the XSLT gets executed, the transformation looks something like the following:

<b>Department: Accounting</b><hr />

1001, Jagadish, 3400<br />

1002, Chatarji, 2500<br />

1003, Winner, 4300<br />

<br />

<b>Department: Sales</b><hr />

2001, Dhanam, 4500<br />

2002, Chinna, 3400<br />

2003, Pedda, 3200<br />

<br />

<b>Department: Research</b><hr />

3001, Ram, 5600<br />

3002, Robert, 5600<br />

3003, Rahim, 4300<br />

<br />

{mospagebreak title=Displaying an HTML table based on the hierarchy}

Now, let us proceed to generate an HTML table based on the XML document we have.  The following is the first approach (using separate templates) we can use to accomplish the same:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <table border="1">

                  <tr>

                        <xsl:apply-templates select ="EmployeeDetails/Department"/>      

                  </tr>

            </table>

      </xsl:template>

      <xsl:template match="EmployeeDetails/Department">

            <td colspan="3">

                  <b>

                        Department: <xsl:value-of select ="@Dname"/>

                  </b>

            </td>

            <xsl:apply-templates select ="Employee"/>

      </xsl:template>

      <xsl:template match ="Employee"     >

            <tr>

                  <td>

                        <xsl:value-of select ="Empno"/>

                  </td>

                  <td>

                        <xsl:value-of select ="Ename"/>

                  </td>

                  <td>

                        <xsl:value-of select ="Sal"/>

                  </td>

            </tr>

      </xsl:template>

     

</xsl:stylesheet>

The total skill lies in identifying and placing the tags appropriately at their respective positions in the templates. The following is the second approach we can use to accomplish the same task (using a nested “xsl:for-each” construct):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

            <table border="1">

                  <xsl:for-each select="EmployeeDetails/Department">

                        <tr>

                              <td colspan="3">

                                    <b>

                                          Department: <xsl:value-of select ="@Dname"/>

                                    </b>

                              </td>

                        </tr>

                        <xsl:for-each select="Employee">

                              <tr>

                                    <td>

                                          <xsl:value-of select ="Empno"/>

                                    </td>

                                    <td>

                                          <xsl:value-of select ="Ename"/>

                                    </td>

                                    <td>

                                          <xsl:value-of select ="Sal"/>

                                    </td>

                              </tr>

                        </xsl:for-each>

                  </xsl:for-each>

            </table>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the above XSLT gets executed, the transformation should look something like the following:

<table border="1">

      <tr>

            <td colspan="3">

                  <b>Department: Accounting</b>

            </td>

      </tr>

      <tr>

            <td>1001</td>

            <td>Jagadish</td>

            <td>3400</td>

      </tr>

      <tr>

            <td>1002</td>

            <td>Chatarji</td>

            <td>2500</td>

      </tr>

      <tr>

            <td>1003</td>

            <td>Winner</td>

            <td>4300</td>

      </tr>

.

.

.

</table>

{mospagebreak title=Searching for an element at a particular location}

In this final section, I would like to deal with the elements available at a particular location. That means I would like provide a transformation for an “nth” element.

To work with the position (or index) of elements, we need to work with an XPath function called “position().”  The following is the first approach we can use to accomplish the task (using separate templates).

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

                  <xsl:apply-templates select ="EmployeeDetails/Department[position() = 2]"/>

      </xsl:template>

      <xsl:template match="EmployeeDetails/Department">

            <b>Department: <xsl:value-of select ="@Dname"/></b><hr />

            <xsl:apply-templates select ="Employee"/>

            <br/>

      </xsl:template>

      <xsl:template match ="Employee"     >

            <xsl:value-of select ="Empno"/>,

            <xsl:value-of select ="Ename"/>,

            <xsl:value-of select ="Sal"/><br />

      </xsl:template>

     

</xsl:stylesheet>

From the above, you can understand that I specified that the position must be 2 (or the second element).  The following is the second approach we can use to deal with the same task (using an “xsl:for-each” construct):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0"

    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/">

                  <xsl:for-each select="EmployeeDetails/Department[position() = 2]">

                        <b>

                              Department: <xsl:value-of select ="@Dname"/>

                        </b>

                        <hr />

                        <xsl:for-each select="Employee">

                              <xsl:value-of select ="Empno"/>,

                              <xsl:value-of select ="Ename"/>,

                              <xsl:value-of select ="Sal"/><br />

                        </xsl:for-each>

                        <br />

                  </xsl:for-each>

      </xsl:template>

</xsl:stylesheet>

In any of the above cases, when the XSLT gets executed, the transformation would look something like the following:

<b>   Department: Sales</b>

<hr />

2001, Dhanam, 4500<br />

2002, Chinna, 3400<br />

2003, Pedda, 3200<br />

<br />

I hope you enjoyed the article and any comments, suggestions, feedback, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com

[gp-comments width="770" linklove="off" ]