Practical Examples

Before we take the plunge into more advanced topics such as using managed objects with XSLT style sheets, let’s recap and summarize what we’ve looked at so far in a couple of real-world examples. First we’ll transform a Microsoft ADO.NET DataSet object into a Microsoft ActiveX Data Objects (ADO) Recordset object. Of course, this transformation will not involve the binary image of the objects, just their XML representation.

Second we’ll look at a Microsoft ASP.NET example to introduce you to the use of a very handy control: the XML Web server control. The XML Web server control is capable of rendering an XML document in the body of a Web page with or without XSLT formatting.

Transforming DataSet Objects into Recordset Objects

Exporting the contents of ADO.NET DataSet objects to legacy ADO applications is a problem that we encountered and solved in Chapter 4. That solution was based on a special breed of XML writer. In this section, we’ll reconsider that approach and use an XSLT style sheet to accomplish the same task.

Bear in mind that using a style sheet to convert a DataSet object to a Recordset object does not necessarily lead to faster code. If we merely consider the transformation process, I do recommend that you always use the writer. Your code is not taxed by the XSLT processor and, perhaps more importantly, you can use a more familiar programming style. The writer is written in C# or Visual Basic and, as such, provides you with total control over the generated output. An XSLT style sheet is something different, even though it is often referred to as a program.

A style sheet is a kind of mask that you put on top of a document to change its appearance; the document can then be saved in its new form. Using a style sheet also decouples the transformation process from the rest of the application. You can modify the logic of the transformation without touching or recompiling a single line of code.

Writing an XSLT style sheet to transform a DataSet object into a Recordset object is useful for other reasons as well. First, the style sheet code needed is not trivial and requires a good working knowledge of both XPath and XSLT. Look at it as a useful exercise to test your level of familiarity with the technologies. Second, you can apply the style sheet directly to the binary DataSet object, without first serializing the object to XML.

The ability to style a binary DataSet object is provided by the XmlDataDocument class. As mentioned in Chapter 6, XmlDataDocument is an XPath document class. It implements the IXPathNavigable interface and, as such, can be directly passed as an argument to the Transform method. (We’ll examine the XmlDataDocument class in detail in Chapter 8.)

Getting the DataSet Object

The following code fetches some records from the Northwind database’s Employees table and stores them into a DataSet object:

string conn = "DATABASE=northwind;SERVER=localhost;UID=sa;";
string comm = "SELECT firstname, lastname, title, notes FROM employees";
SqlDataAdapter adapter = new SqlDataAdapter(comm, conn);
DataSet data = new DataSet("Northwind");
adapter.Fill(data, "Employees");

The DataSet object is named Northwind and contains just one DataTable object, Employees. As we’ll see in a moment, the names of the DataSet and DataTable objects play a key role in the XML representation of the objects. By default, a DataSet object is named NewDataSet, and a DataTable object is named Table. (We’ll look at ADO.NET XML serialization in great detail in Chapter 9 and Chapter 10.)

The XML representation of a DataSet object looks like this:

<DataSetName>
  <TableName>
    <employeeid>...</employeeid>
    <lastname>...</lastname>
    ⋮
  </TableName>
  ⋮
</DataSetName>

Tip

You can get the string representing the XML version of the DataSet object through the DataSet method GetXml. The text does not include schema information. You can get the schema script separately by calling the GetXmlSchema method. To persist the XML representation to a stream, use the WriteXml method instead.


Transforming the DataSet Object

Transforming a DataSet object into a Recordset object poses a couple of problems. The first is that you have to infer and write the Recordset object’s schema. The second is that the XML layout of the DataSet object depends on a number of different parameters. In particular, the root of the XML version of the DataSet object depends on the object’s DataSetName property. Likewise, each table record is grouped under a node whose name matches the DataTable object’s TableName property.

You could easily work around the first issue by writing a more generic XSLT script. As for the second problem, because a DataSet object can contain multiple tables, you must necessarily know the name of the table you want to process and render as a Recordset object. The name of the table must be passed to the XSLT processor through the argument list.

The following code shows how to transform the DataSet object into an XPath document and load it into the processor. The result of the transformation is directly written out to an auto-indent XML writer. The argument passed to the style sheet is the name of the first table in the specified DataSet object.

// Set up the style sheet
XslTransform xslt = new XslTransform();
xslt.Load("ado.xsl");

// Create an XPath document from the DataSet
XmlDataDocument doc = new XmlDataDocument(data);

// Prepare the output writer
XmlTextWriter writer = new XmlTextWriter(outputFile, null);
writer.Formatting = Formatting.Indented;
            
// Set some arguments
XsltArgumentList args = new XsltArgumentList();
args.AddParam("TableName", "", data.Tables[0].TableName);

// Call the transfomer and close the writer upon completion
xslt.Transform(doc, args, writer);
writer.Close();

The XmlDataDocument class internally creates an XML DOM representation of the DataSet content. That content then becomes the input for the XSLT style sheet.

The ADO Style Sheet

Let’s analyze the XSLT code necessary to transform a DataSet object into the XML version of an ADO Recordset object. The following listing shows the overall layout:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />

<!-- Matches the DataSet’s root, whatever the name -->
<xsl:template match="/child::*[position()=1]">

  <!-- PARAM:: Name of the table to consider -->
  <xsl:param name="TableName" select="string(‘Table’)" />

  <!-- The XML-based ADO Recordset  -->
  ⋮
  <!-- End of the XML-based ADO Recordset  -->

  </xsl:template>
</xsl:stylesheet>

The style sheet contains a single template that applies to the first node in the document—that is, the DataSet object’s root. Because the match is found using a generic XPath expression that selects the first child, the template will work on the DataSet object’s root, whatever its name might be.

The style sheet can accept one argument (TableName) that defaults to the string Table. Note that if you omit the XPath string function, Table denotes a node-set value rather than a string.

The XML version of an ADO Recordset object consists of two distinct blocks—schema and rows—grouped under an <xml> node. Here’s the code for the Recordset schema:

<xml 
    xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" 
    xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" 
    xmlns:rs="urn:schemas-microsoft-com:rowset" 
    xmlns:z="#RowsetSchema">
        
  <!-- Create the schema -->
  <xsl:element name="s:schema" 
      namespace="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882">
    <xsl:attribute name="id">RowsetSchema</xsl:attribute>

    <xsl:element name="s:ElementType" 
        namespace="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882">
      <xsl:attribute name="name">row</xsl:attribute> 
      <xsl:attribute name="content">eltOnly</xsl:attribute> 
                
      <!-- Take the first table tree and walk its children 
           to enumerate the fields in the schema -->
      <xsl:for-each 
          select="child::*[local-name()=$TableName][position()=1]">
        <xsl:for-each select="child::*">
          <xsl:element name="s:AttributeType"
              namespace="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882">
            <xsl:attribute name="name">
              <xsl:value-of select="local-name()" />
            </xsl:attribute> 
          </xsl:element>    
        </xsl:for-each>
      </xsl:for-each>
    </xsl:element> 
            
    <xsl:element name="s:extends" 
        namespace="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882">
      <xsl:attribute name="type">rs:rowbase</xsl:attribute> 
    </xsl:element> 
  </xsl:element>

After you create the <xml> node with all of its required namespace declarations, you create a <s:schema> node with an id attribute. The schema tree contains the definitions of all the element and attribute types that will be used later. Note that ADO expresses the Recordset object in XML using the XML-Data Reduced (XDR) schema instead of the newer XML Schema Definition (XSD) schema. (See Chapter 3.)

In particular, the Recordset schema defines a <row> element to render a table row. The node will contain as many attributes as there are columns in the source table. To define all the attributes in the Recordset schema, you must visit all the children of a <TableName> node in the DataSet object. The actual name of the <TableName> node will be specified by the $TableName style sheet argument.

The sample listing emphasizes a couple of for-each statements. The first statement selects the first node whose local, unqualified name matches the $TableName argument. The second loop enumerates the children of this node and creates an attribute schema definition for each.

The final step involves the creation of the data rows. Each source row corresponds to a <z:row> node whose attributes map to the source columns, as shown here:

<xsl:element name="rs:data" 
    namespace="urn:schemas-microsoft-com:rowset">
  <xsl:for-each select="child::*[local-name()=$TableName]" >
    <xsl:element name="z:row" namespace="#RowsetSchema">
      <xsl:for-each select="child::*">
        <xsl:attribute name="{local-name()}">
          <xsl:value-of select="." />
        </xsl:attribute>
      </xsl:for-each>
    </xsl:element>
  </xsl:for-each>
</xsl:element>

This listing also includes a couple of nested for-each statements that run in the context of the DataSet object’s root. The outer loop selects all the nodes whose name matches the $TableName parameter, whereas the innermost loop creates an attribute for each child node found. The <z:row> node is expected to have as many attributes as the child nodes of the corresponding source tree and be named after them. In other words, the name of the attribute must be determined dynamically.

In an XSLT script, you create an attribute using the <xsl:attribute> instruction. The instruction has a name attribute to let you assign a name to the attribute. The name attribute can only be set with a literal, however. What if you must use an XPath expression to decide the name? In that case, you use the following special XPath syntax:

<xsl:attribute name="{local-name()}">

By wrapping the expression in curly brackets, you tell the processor that the attribute must be assigned the result of the specified expression.

Figure 7-11 illustrates a sample application that runs a query against SQL Server and saves the output in ADO-compliant XML.

Figure 7-11. The DataSet-to-Recordset style sheet converter in action.


Caution

The style sheet discussed in this example works well even if the DataSet object contains multiple tables. In fact, it has been designed to process only the nodes that match a given table name. The style sheet will produce incorrect XML output if a relationship exists between two tables and the corresponding DataRelation object has the Nested property set to true. In this case, the records of the child table are serialized below each parent row, thus resulting in a discrepancy between the declared schema and the actual contents of each row.

A possible workaround is to use a second parameter, n, that specifies the number of columns in the table to be processed. While you define the schema, you stop the loop after the first n child rows, discarding all the rows set there because of the nested relationship.


The XML Web Server Control

The XML Web server control is used to output the contents of an XML document directly in an ASP.NET page. The control can display the source XML as is or as the results of an XSLT transformation.

The XML Web server control, denoted by the <asp:xml> tag, is a declarative counterpart to the XslTransform class. The XML Web server control has no more features than the XslTransform class. More precisely, the XML Web server control makes use of the XslTransform class internally.

You use the XML Web server control when you need to embed XML documents in a Web page. For example, the control is extremely handy when you need to create XML data islands for the client to consume. Data islands consist of XML data referenced or included in an HTML page. The XML data can be included in-line within the HTML, or it can be in an external file. By combining this control’s ability with the ADO XML style sheet we created in the previous section, you can transform a DataSet object into an ADO Recordset object and send it to the browser to be processed by client script procedures.

Let’s take a closer look at the programming interface of the XML Web server control.

Programming the XML Web Server Control

In addition to the typical and standard properties of all server controls, the XML Web server control provides the properties listed in Table 7-9. The document properties represent the source XML data, and the transform properties handle the instance of the XslTransform class to be used and the style sheet.

Table 7-9. Properties of the XML Web Server Control
Property Description
Document Sets the XML source document using an XmlDocument object
DocumentContent Sets the XML source document using a string
DocumentSource Sets the XML source document using a file
Transform Sets the XslTransform class to use for transformations
TransformArgumentList Gets or sets the argument list for transformations
TransformSource Sets the style sheet to use for transformations

You can specify a source document using a file, a string, or an XML DOM object. A style sheet, on the other hand, can be specified using a file or a preconfigured XslTransform object. The output of the transformation, if any, is the Web page output stream.

The settings are mutually exclusive, and the last setting always wins. For example, if you set both Document and DocumentSource, no exception is thrown, but the first assignment is overridden. Although Table 7-9 emphasizes the writing of these properties, they are all read/write properties. For the DocumentContent property, however, only the set accessor has a significant implementation. If you attempt to read the property, an empty string is returned.

The DocumentContent property can be set programmatically by using a string variable or declaratively by placing text between the start and end tags of the control, as shown here:

<asp:xml runat="server" id="theXml">
  ... xml data ...
</asp.xml>

You can optionally specify an XSL style sheet document that formats the XML document before it is written to the output. The output of the style sheet must be HTML, XML, or plain text. It can’t be, for example, ASP.NET source code or a combination of ASP.NET layout declarations. Let’s look at a few practical examples.

Server-Side Transformations

The following listing demonstrates a simple but effective way to describe a portion of your Web page using XML code. The actual XML-to-HTML transformation is automatically and silently performed by the style sheet.

<!-- Show employee info -->
<asp:xml runat="server" TransformSource="EmpInfo.xsl">
  <MyDataSet>
    <NorthwindEmployees>
      <Employee>
        <employeeid>1</employeeid>
        <firstname>Nancy</firstname>
        <lastname>Davolio</lastname>
        <title>Sales Representative</title>
        <notes>...</notes>
      </Employee>
    </NorthwindEmployees>
  </MyDataSet>  
</asp:xml>
							

The XML Web server control can have an ID and can be programmatically accessed. This opens up a new possibility. You can now check the browser’s capabilities and decide dynamically which style sheet is most appropriate.

You can also describe the entire page with XML and use a style sheet to translate the page into HTML, as shown in the following code. This is not always, and not necessarily, the best solution to gain flexibility, but the XML Web server control definitely makes implementing that solution considerably easier.

<asp:xml runat="server" 
    DocumentSource="Employees.xml"
    TransformSource="EmpInfo.xsl" />

If you need to pass in an argument, simply create and populate an instance of the XsltArgumentList class and pass it to the control using the TransformArgumentList property.

Creating Client-Side Data Islands

A data island is a block of data that is embedded in the body of an HTML page and is invisible to the user. Storing data in hidden fields is certainly the oldest and more widely supported way of implementing data islands. You can think of XML data islands as islands of XML data dispersed in the sea of HTML pages.

Modern browsers (Internet Explorer 5.0 and later) support an ad hoc client-side tag, <xml>, to store islands of data, hiding them from view, as shown here:

<xml id="data">
  ... XML data goes here ... 
</xml>

Don’t confuse the Internet Explorer 5.0 client-side HTML tag with the <asp:xml> server-side control. In Chapter 14, we’ll return to data islands, and you’ll learn how to define them from within server pages. For now, let’s just say that an XML data island is XML text wrapped in an <xml> HTML tag. Not all browsers support this. The example described here requires Internet Explorer 5.0 or later.

Used in conjunction with the <xml> tag, the XML Web server control can be very helpful and effective. The following code flushes the contents of the specified XML file in a particular data island:

<xml id="data">
    <asp:xml runat="server" documentsource="employees.xml" />
</xml>

If needed, you can first apply a transformation. For example, you can embed an ADO XML Recordset object in a data island. In this case, set the TransformSource property of the XML Web server control with the proper style sheet.

Internet Explorer 5.0 automatically exposes the contents of the <xml> tag through an XML DOM object. Hold on, though—that’s not managed code! What you get is a scriptable MSXML COM object. The following ASP.NET page includes some VBScript code that retrieves the contents of the data island. (More on this in Chapter 14.)

<script runat="server">
<!-- Add a client-side onclick handler to the button -->
void Page_Load(object sender, EventArgs e)
{
    button.Attributes["onclick"] = "ReadXmlData()";
}
</script> 

<html>
  <script language="VBScript">
  Sub ReadXmlData()
      ’ data is the name of the <xml> tag and 
      ’ represents an MSXML XML DOM object
      window.alert(data.DocumentElement.nodeName)
  End Sub
  </script>

  <body>
    <h1>Client-side Data Islands</h1>

    <!-- Client-side XML data island -->
    <xml id="data">
      <asp:xml runat="server" documentsource="employees.xml" />
    </xml>
    <!-- End of the data island -->

    <form runat="server">
      <asp:button runat="server" id="button" text="Click..." />
    </form>

  </body>
</html>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.190.41