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.
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.
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> 
</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
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> 
</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.