Recipe15.8.Transforming XML


Recipe 15.8. Transforming XML

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 place it in a comma-delimited text file for legacy system integration. 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 as well as into the comma-delimited format.

Solution

The solution for this is to use an XSLT stylesheet to transform the XML into another format using the XslCompiledTransform class. In the example code, you transform some personnel data from a fictitious business stored in Personnel.xml. First, load the stylesheet for generating HTML output, then perform the transformation to HTML via XSLT using the PersonnelHTML.xsl stylesheet. After that, 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.     XslCompiledTransform transform = new XslCompiledTransform();     XsltSettings settings = new XsltSettings();     // Disable both of these (the default) for security reasons.     settings.EnableDocumentFunction = false;     settings.EnableScript = false;     // Load up the stylesheet.     transform.Load(@"..\..\PersonnelHTML.xsl",settings,resolver);     // Perform the transformation.     transform.Transform(@"..\..\Personnel.xml",@"..\..\Personnel.html");     // Or transform the Personnel.xml file to comma-delimited format.     // Load up the stylesheet.     transform.Load(@"..\..\PersonnelCSV.xsl",settings,resolver);     // Perform the transformation.     transform.Transform(@"..\..\Personnel.xml",         @"..\..\Personnel.csv"); } 

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> 

To generate the HTML screen in Figure 15-1, use the PersonnelHTML.xsl stylesheet and the Personnel.xml file.

Figure 15-1. Personnel HTML table generated from Personnel.xml


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> 

To generate comma-delimited output, use 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 XslCompiledTransform.Transform method. Since XmlResolver is an abstract class, you need to use either the XmlUrlResolver or 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 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/line feed combination in the XSLT output was such a trial that we were able to find more than 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 line feed. This is output at the end of each employee's data from the XML.

See Also

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



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net