DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 17.8 Transforming XML to HTML

Problem

You have a raw XML document that you need to convert into a more readable format. For example, you have personnel data that is stored as an XML document and you need to display it on a web page or in a text file. Unfortunately, not everyone wants to sort through reams of XML all day; they would rather read the data as a formatted list or within a grid with defined columns and rows. You need a method of transposing the XML data into a more readable form.

Solution

The solution for this is to use an XSLT stylesheet to transform the XML into another format using the XslTransform class. In the example code, we are transforming some personnel data from a fictitious business stored in Personnel.xml. First, we load the stylesheet for generating HTML output, then we perform the transformation to HTML via XSLT using the PersonnelHTML.xsl stylesheet. After that, we transform the data to comma-delimited format using the PersonnelCSV.xsl stylesheet:

public static void TransformXML( )
{
    // Create a resolver with default credentials.
    XmlUrlResolver resolver = new XmlUrlResolver( );
    resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

    // transform the personnel.xml file to html

    XslTransform transform = new XslTransform( );
    // load up the stylesheet
    transform.Load(@"..\..\PersonnelHTML.xsl",resolver);
    // perform the transformation
    transform.Transform(@"..\..\Personnel.xml",@"..\..\Personnel.html",resolver);

    // transform the personnel.xml file to comma delimited format
    
    // load up the stylesheet
    transform.Load(@"..\..\PersonnelCSV.xsl",resolver);
    // perform the transformation
    transform.Transform(@"..\..\Personnel.xml",
      @"..\..\Personnel.csv",resolver);

}

The Personnel.xml file contains the following items:

<?xml version="1.0" encoding="utf-8"?>
<Personnel>
    <Employee name="Bob" title="Customer Service" companyYears="1"/>
    <Employee name="Alice" title="Manager" companyYears="12"/>
    <Employee name="Chas" title="Salesman" companyYears="3"/>
    <Employee name="Rutherford" title="CEO" companyYears="27"/>
<Personnel>

The PersonnelHTML.xsl stylesheet looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:template match="/">
      <html>
        <head />
          <body title="Personnel">
            <xsl:for-each select="Personnel">
              <p>
                <xsl:for-each select="Employee">
                    <xsl:if test="position( )=1">
                        <table border="1">
                            <thead>
                                <tr>
                                  <td>Employee Name</td>
                                  <td>Employee Title</td>
                                  <td>Years with Company</td>
                                </tr>
                            </thead>
                            <tbody>
                              <xsl:for-each select="../Employee">
                                <tr>
                                  <td>
                                    <xsl:for-each select="@name">
                                        <xsl:value-of select="." />
                                    </xsl:for-each>
                                  </td>
                                  <td>
                                    <xsl:for-each select="@title">
                                        <xsl:value-of select="." />
                                    </xsl:for-each>
                                  </td>
                                  <td>
                                    <xsl:for-each select="@companyYears">
                                        <xsl:value-of select="." />
                                    </xsl:for-each>
                                  </td>
                                </tr>
                              </xsl:for-each>
                            </tbody>
                        </table>
                    </xsl:if>
                </xsl:for-each>
              </p>
            </xsl:for-each>
        </body>
      </html>
    </xsl:template>
</xsl:stylesheet>

The output from the PersonnelHTML.xsl stylesheet and the Personnel.xml generates the HTML shown in Figure 17-1.

Figure 17-1. Personnel HTML table generated from Personnel.xml
figs/cscb_1701.gif

Here is the HTML source:

<html xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body title="Personnel">
    <p>
      <table border="1">
        <thead>
          <tr>
            <td>Employee Name</td>
            <td>Employee Title</td>
            <td>Years with Company</td>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Bob</td>
            <td>Customer Service</td>
            <td>1</td>
          </tr>
          <tr>
            <td>Alice</td>
            <td>Manager</td>
            <td>12</td>
          </tr>
          <tr>
            <td>Chas</td>
            <td>Salesman</td>
            <td>3</td>
          </tr>
          <tr>
            <td>Rutherford</td>
            <td>CEO</td>
            <td>27</td>
          </tr>
        </tbody>
      </table>
    </p>
  </body>
</html>

The comma-delimited output is generated using PersonnelCSV.xsl and Personnel.xml; the stylesheet is shown here:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:
xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" encoding="UTF-8"/>
    <xsl:template match="/">
                <xsl:for-each select="Personnel">
                    <xsl:for-each select="Employee">
                        <xsl:for-each select="@name">
                            <xsl:value-of select="." />
                        </xsl:for-each>,<xsl:for-each select="@title">
                            <xsl:value-of select="." />
                        </xsl:for-each>,<xsl:for-each select="@companyYears">
                            <xsl:value-of select="." />
                        </xsl:for-each>
                <xsl:text> &#xd;&#xa;</xsl:text>
               </xsl:for-each>
                </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

The output from the PersonnelCSV.xsl stylesheet is shown here:

Bob,Customer Service,1 
Alice,Manager,12 
Chas,Salesman,3 
Rutherford,CEO,27

Discussion

There are many overrides for the XslTransform.Transform method. As of .NET 1.1, the majority of these are now marked as obsolete because they do not take an XmlResolver as one of the parameters. If you attempt to use one of these overloads without an XmlResolver, the compiler will issue a warning:

xml.cs(354,13): warning CS0618: 'System.Xml.Xsl.XslTransform.Transform(string, 
string)' is obsolete: 'You should pass XmlResolver to Transform( ) method'

Since XmlResolver is an abstract class, you need to either use the XmlUrlResolver, the XmlSecureResolver, or pass null as the XmlResolver typed argument. The XmlUrlResolver will resolve URLs to external resources, such as schema files, using the file, http, and https protocols. The XmlSecureResolver restricts the resources that you can access by requiring you to pass in evidence, which helps prevent cross-domain redirection in XML. If you are accepting XML from the Internet, it could easily have a redirection in it to a site where malicious XML would be waiting to be downloaded and executed if you were not using the XmlSecureResolver. If you pass null for the XmlResolver, you are saying you do not want to resolve any external resources. Microsoft has declared the null option to be obsolete, and it shouldn't be used anyway since you should always use some type of XmlResolver.

XSLT is a very powerful technology that allows you to transform XML into just about any format you can think of, but it can be frustrating at times. The simple need of a carriage return/linefeed combination in the XSLT output was such a trial that we were able to find over 20 different message board requests for help on how to do this! After looking at the W3C spec for XSLT, we found you could do this using the xsl:text element like this:

<xsl:text> &#xd;&#xa;</xsl:text>

The & xd; stands for a hexadecimal 13, or a carriage return, and the & xa; stands for a hexadecimal 10, or a linefeed. This is output at the end of each employee's data from the XML.

See Also

See the "XslTransform Class," "XmlResolver Class," "XmlUrlResolver Class," "XmlSecureResolver Class," and "xsl:text" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section