Microsoft.NET

……………………………………………….Expertise in .NET Technologies

Working with XML data in ASP.NET

Posted by Ravi Varma Thumati on May 8, 2009

Table of Contents

  • Overview of the XML Classes
  • Using XML with DataSets
  • Transforming XML with XSL Stylesheets
  • Using the ASP.NET Xml Control
  • Using the XslTransform Class
  • Using Strongly Typed DataSets

In this chapter, you will learn how to use the eXtensible Markup Language (XML) with DataSets and ASP.NET controls. First, you will learn how to represent XML data in a DataSet. We will discuss how to load XML data directly into a DataSet by using the ReadXml method. And also learn how to mirror the XML data in a DataSet by using an XmlDataDocument class. 

Next, we will learn two methods of transforming XML documents with eXtensible Stylesheet Language (XSL) stylesheets. First, we will examine how to use XSL stylesheets with the ASP.NET Xml control, which enables us to transform XML into other document formats such as HTML. And also learn how to use the XslTransform class to perform the same tasks independently of the Xml control.

Finally, examine how to use an XML schema to generate a strongly typed DataSet. You can use a strongly typed DataSet to provide a more intuitive representation of data.

Overview of the XML Classes

The .NET framework contains a rich set of classes for working with XML data; these classes are divided into multiple namespaces. This book does not cover all these classes; however, I want to provide you with an overview of the most important ones.

The main classes for working with XML data are as follows:

  • XmlTextReader— this class provides fast, forward-only access to the raw data contained in an XML file. It parses XML data into tokens, but it does not represent the XML data in an object model such as the W3C XML Document Object Model (DOM).
  • XmlTextWriter— this class provides a fast, forward-only method to write data to an XML file. It ensures that the data written to the file conforms to the W3C XML 1.0 standard.
  • XmlDocument— this class represents XML using the W3C XML DOM levels 1 and 2. You can use this class to both navigate and edit the nodes in an XML document tree.
  • XmlDataDocument— this class enables you to represent both XML and relational data in W3C XML DOM levels 1 and 2. You can use this class with a DataSet to provide both relational and non-relational views of the same underlying data.
  • XmlNodeReader— this class provides fast, forward-only access to data represented by the XmlDocument or XmlDataDocument class.
  • DocumentNavigator— this class enables you to efficiently navigate an XML document represented by the XmlDocument class. It supports the XPath data model for navigation.
  • DataDocumentNavigator— this class enables you to efficiently navigate an XML document represented by the XmlDataDocument class. It supports the XPath data model for navigation.
  • XslTransform— this class enables you to transform an XML document with XSL stylesheets. It supports XSLT 1.0 stylesheet syntax.

These XML classes can be roughly divided into four types. The first two classes, XmlTextReader and XmlTextWriter, enable you to perform the basic operations of reading and writing XML data to a file.

If you need to represent XML data in a static, memory-resident structure, you use either the XmlDocument or XmlDataDocument class. These classes enable you to create an XML DOM to represent an XML document in a memory-resident tree.

NOTE

To learn more details about the XML Document Object Model (DOM), visit the World Wide Web Consortium (W3C) Web site at http://www.w3.org/dom.

The XmlDocument class enables you to represent XML data in a DOM. XmlDataDocument extends the XmlDocument class to enable you to represent the XML data in either a DOM or a DataSet.

If you want to efficiently navigate the nodes of an XML document represented by the XmlDocument or XmlDataDocument class, you can use the XmlNodeReader, DocumentNavigator, or DataDocumentNavigator class. The latter two classes support XPath expressions.

Finally, the XslTransform class enables you to perform transformations on an XML document. Typically, you use the XslTransform class either to convert one XML document into another XML document or to convert an XML document into an HTML document. However, you also can use the XslTransform class to convert an XML document into any other file format.

NOTE

To learn more details about XSL (eXtensible Stylesheet Language), visit the W3C Web site at http://www.w3.org/style/xsl.

Using XML with DataSets

In, “Working with DataSets,” you learned how to use a DataSet to represent data retrieved from a database such as Microsoft SQL Server. A DataSet can also be used to represent XML data.

A DataSet can represent XML data in two ways: You can load XML data directly into a DataSet, or you can build an XmlDataDocument class from an existing DataSet. In the following sections, you examine both ways of working with XML data.

Reading an XML Document into a DataSet

You can read an XML file directly from the hard drive into a DataSet by using the ReadXml () method of the DataSet class. For example, imagine that you want to represent the XML document contained in Listing 13.1 with a DataSet.

Listing 13.1 Menu.xml
<Menu>
  <MenuItem>
    <Food>
      French Toast
    </Food>
    <Price>
      12.45
    </Price>
  </MenuItem>
  <MenuItem>
    <Food>
      Scrambled Eggs
    </Food>
    <Price>
      3.89
    </Price>
  </MenuItem>
</Menu>

Listing 13.1 contains a simple XML file that represents a menu from a restaurant. The menu contains two menu items. The first item on the menu is French Toast, which costs $12.45, and the second item is Scrambled Eggs, which costs $3.89.

The ASP.NET page in Listing 13.2 demonstrates how you can read the Menu.xml file into a DataSet

Listing 13.2 ReadMenu.aspx
<%@ Import Namespace="System.Data" %>
<Script Runat="Server">
Sub Page_Load
  Dim dstMenu As DataSet
  dstMenu = New DataSet()
  dstMenu.ReadXml( MapPath( "Menu.xml" ) )
  dgrdMenu.DataSource = dstMenu
  dgrdMenu.DataBind()
End Sub
</Script>
<html>
    <head><title>ReadMenu.aspx</title></head>
       <body>
           <asp:DataGrid cellpadding="10" Runat="Server" />
       </body>
</html>

In the Page_Load subroutine in Listing 13.2, an instance of the DataSet class is created. Next, the ReadXml() method is called to read the Menu.xml file from the hard drive. The DataSet is bound to the DataGrid control, and the menu is displayed.

NOTE

You might notice that the MapPath method is used in Listing 13.2 with the ReadXml () method. The MapPath() method translates a virtual file path, such as /xmlfiles/menu.xml, into a physical file path, such as c:\inetpub\wwwroot\_xmlfiles\menu.xml. The ReadXml () method expects a physical path.

You can use the same method to bind XML data to any of the standard ASP.NET controls that have a DataSource property. For example, you could bind an XML file that contains a list of countries to a DropDownList or ListBox control.

Using a Schema with ReadXml

In the preceding section, you learned how to use the ReadXml () method to load an XML file into a DataSet. You might have been surprised that you did not have to supply either a Document Type Definition (DTD) or XML schema when loading the XML file. How does the DataSet determine the structure of the XML document? It makes its best guess.

When you read the XML file into the DataSet, the DataSet infers the structure of the XML file. For example, it checks whether the elements directly under the root node can be interpreted as tables. If they can be, the root node is interpreted as a DataSet. Otherwise, the root node represents a table.

There are some significant disadvantages to allowing the DataSet to guess the structure of an XML document. One problem is that the DataSet must interpret all the elements as representing strings. So, even the Price element in the Menu.xml file in Listing 13.1 is interpreted as a string, when it should, more accurately be represented as a decimal.

If you are simply displaying the XML file in a control such as a DataGrid, you might not care how the value of the element is represented. However, in certain applications, you want the data types of the elements to be correctly represented. To do so, you need to supply an XML schema with the XML file.

NOTE

To learn more details about XML schemas, visit the W3C Web site at http://www.w3.org/xml/schema.

You can specify a schema with an XML file in two ways. You can include the schema in the same document as the XML file, or you can read the schema separately by using the ReadXmlSchema () method.

The file in Listing 13.3 contains both a schema and XML data in the same file.

Listing 13.3 SchemaData.xml
<Menu>
  <xsd:schema
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element>
      <xsd:complexType>
        <xsd:all>
          <xsd:element minOccurs="0"/>
          <xsd:element minOccurs="0"/>
        </xsd:all>
      </xsd:complexType>
    </xsd:element>
    <xsd:element msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element ref="MenuItem"/>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <MenuItem>
    <Food>
      French Toast
    </Food>
    <Price>
      12.45
    </Price>
  </MenuItem>
  <MenuItem>
    <Food>
      Scrambled Eggs
    </Food>
    <Price>
      3.89
    </Price>
  </MenuItem>
</Menu>

The schema in Listing 13.3 specifies the data types for both the Food and Price elements: The Food element contains string values, and the Price element contains decimal values.

The ASP.NET page in Listing 13.4 displays all the menu items from the SchemaData.xml file by using a Repeater control.

Listing 13.4 ReadSchemaData.aspx
<%@ Import Namespace="System.Data" %>
<Script Runat="Server">
Sub Page_Load
  Dim dstMenu As DataSet
  dstMenu = New DataSet()
  dstMenu.ReadXml( MapPath( "SchemaData.xml" ) )
  rptMenu.DataSource = dstMenu
  rptMenu.DataBind()
End Sub
</Script>
<html>
<head><title>ReadSchemaData.aspx</title></head>
<body>
<asp:Repeater Runat="Server" >
<ItemTemplate>
  <hr>
  <p><b>Food:</b>
  <br><%# Container.DataItem( "Food" )%>
  <p><b>Food Data Type:</b>
  <br><%# Container.DataItem( "Food" ).GetType %>
  <p><b>Price:</b>
  <br><%# Container.DataItem( "Price" )%>
  <p><b>Price Data Type:</b>
  <br><%# Container.DataItem( "Price" ).GetType %>
</ItemTemplate>
</asp:Repeater>
</body>
</html>

In the Page_Load subroutine in Listing 13.4, the ReadXml () method retrieves the SchemaData.xml file into the DataSet. The DataSet is then bound to a Repeater control, which displays the values of all the elements.

Additionally, to illustrate that the Food column is represented as a string and the Price column is represented as a decimal, the data type of each element is displayed by the Repeater control. The data type is returned with the GetType() method.

If you prefer, you can place an XML schema in a separate file and read the schema by using the ReadXmlSchema() method instead. For example, Listing 13.5 contains the schema for the Menu.xml file.

Listing 13.5 MenuSchema.xml
<xsd:schema
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xsd:element>
    <xsd:complexType>
      <xsd:all>
        <xsd:element minOccurs="0"/>
        <xsd:element minOccurs="0"/>
      </xsd:all>
    </xsd:complexType>
  </xsd:element>
  <xsd:element msdata:IsDataSet="true">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:element ref="MenuItem"/>
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

The page in Listing 13.6 illustrates how you can read the MenuSchema.xml file by using the ReadXmlSchema() method.

Listing 13.6 ReadSchema.aspx
<%@ Import Namespace="System.Data" %>
<Script Runat="Server">
Sub Page_Load
  Dim dstMenu As DataSet
  dstMenu = New DataSet()
  dstMenu.ReadXmlSchema( MapPath( "MenuSchema.xml" ) )
  dstMenu.ReadXml( MapPath( "menu.xml" ) )
  dgrdMenu.DataSource = dstMenu
  dgrdMenu.DataBind()
End Sub
</Script>
<html>
<head><title>ReadSchema.aspx</title></head>
<body>
<asp:DataGrid
  cellpadding="10"
  Runat="Server" />
</body>
</html>

The ASP.NET page in Listing 13.6 uses both the MenuSchema.xml and Menu.xml files to load the XML data. After the XML data is retrieved into the DataSet, the DataSet is bound to a DataGrid control, and the XML data is displayed.

Writing an XML Document from a DataSet

The DataSet class includes several methods for retrieving an XML representation of the data contained in a DataSet. You can use these methods regardless of whether you originally loaded the DataSet with data from an XML file or from a database table.

If you want to retrieve a string representation of XML data, you can use the GetXml() method. For example, the page in Listing 13.7 uses the GetXml() method to output an XML representation of a DataSet to the browser.

Listing 13.7 GetXml.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim conPubs As SqlConnection
  Dim dadTitles As SqlDataAdapter
  Dim dstTitles As DataSet
  Dim strXmlData As String
 
  conPubs = New SqlConnection( "Server=localhost;UID=sa;PWD=secret;Database=Pubs" )
  dadTitles = New SqlDataAdapter( "Select * From Titles", conPubs )
  dstTitles = New DataSet()
  dadTitles.Fill( dstTitles, "Titles" )
 
  strXmlData = dstTitles.GetXml()
  Response.Write( "<pre>" & Server.HtmlEncode( strXmlData ) & "</pre>" )
End Sub
 
</Script>

In Listing 13.7, a DataSet is populated from a SQL Server database table named Titles. After the Titles table is added to the DataSet, the GetXml() method retrieves a string that contains an XML representation of the DataSet. This string is assigned to a variable named strXmlData and outputted using the Response object.

NOTE

The Server.HtmlEncode() method in Listing 13.7 converts the < and > characters to HTML-safe character entities so they can be properly displayed in a browser.

The GetXml() method actually retrieves two things: the XML data itself and the XML schema for the DataSet. If you want to retrieve only the XML schema, not the XML data, you can use the GetXmlSchema() method.

The DataSet class also contains two methods for writing an XML representation of the DataSet to a stream: WriteXml() and WriteXmlSchema().

The page in Listing 13.8, for example, illustrates how you can write an XML representation of a DataSet to a file named myXml.xml by using the WriteXml() method.

Listing 13.8 WriteXml.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim conPubs As SqlConnection
  Dim dadTitles As SqlDataAdapter
  Dim dstTitles As DataSet
 
  ConPubs = New SqlConnection("Server=localhost;UID=sa;PWD=secret;Database= Pubs" )
  dadTitles = New SqlDataAdapter( "Select * From Titles", conPubs )
  dstTitles = New DataSet()
  dadTitles.Fill( dstTitles, "Titles" )
 
  dstTitles.WriteXml( "c:\myXml.xml" )
  Response.Write( "Saved myXml.xml file!" )
End Sub
 
</Script>

In Listing 13.8, the DataSet that is created contains the Titles table from the Pubs database. An XML representation of the DataSet is written to a file named myXml.xml located at the root of the C: drive.

The WriteXml() method accepts an additional parameter that specifies how the XML data should be saved. This parameter can accept any of the following values of the XmlWriteMode enumeration:

  • DiffGram— Writes the XML data so that it can be used with SQL Server 2000 UpdateGrams
  • IgnoreSchema— Writes the XML data without including the XML schema
  • WriteSchema— Writes both the XML data and the XML schema (the default)

The page in Listing 13.9, for example, writes an XML representation of a DataSet to the Response object’s output stream. The XmlWriteMode parameter is assigned the value IgnoreSchema to display only the XML data. On recent versions of Internet Explorer (for example, Internet Explorer 5.0 and higher), the XML file is displayed directly in the browser ().

Listing 13.9 WriteXMLResponse.aspx
<%@ Page ContentType="text/xml" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim conPubs As SqlConnection
  Dim dadTitles As SqlDataAdapter
  Dim dstTitles As DataSet
 
  conPubs = New SqlConnection( "Server=localhost;UID=sa;PWD=secret;Database= Pubs" )
  dadTitles = New SqlDataAdapter( "Select * From Titles", conPubs )
  dstTitles = New DataSet()
  dadTitles.Fill( dstTitles, "Titles" )
 
  dstTitles.WriteXml( Response.OutputStream, XmlWriteMode.IgnoreSchema )
End Sub
 
</Script>

CAUTION

A page directive at the top of Listing 13.9 specifies the content type of the output of the page. Internet Explorer has an unpleasant tendency to cache the content type of a page. So, if you open the page once in Internet Explorer without specifying the content type correctly, you’ll have a difficult time displaying the page correctly in the browser thereafter. If you get desperate, you can force Internet Explorer to recognize a new page content type by renaming the page.

Using XMLDataDocuments with DataSets

In the previous sections, you learned how to load an XML document into a DataSet. A DataSet provides you with a relational view of the XML data; all the information is represented with tables, rows, and columns.

In certain situations, however, working with a non-relational view of the data contained in a DataSet is more convenient. In other words, sometimes it makes more sense to represent data as nodes on a tree rather than as tables, rows, and columns.

Consider, for example, the XML document in Listing 13.10.

Listing 13.10 Recipes.xml
<RecipeList>
  <Recipe>
    <Name>
      French Toast
    </Name>
    <Ingredients>
      <Ingredient>
        Bread
      </Ingredient>
      <Ingredient>
        Butter
      </Ingredient>
      <Ingredient>
        Sugar
      </Ingredient>
    </Ingredients>
  </Recipe>
  <Recipe>
    <Name>
      Tomato Soup
    </Name>
    <Ingredients>
      <Ingredient>
        Tomatoes
      </Ingredient>
      <Ingredient>
        Water
      </Ingredient>
    </Ingredients>
  </Recipe>
</RecipeList>

The XML document in Listing 13.10 represents a list of recipes. It contains recipes for French Toast and Tomato Soup. Each recipe, in turn, contains a list of ingredients.

In the Recipes.xml document, notice that the <Ingredients> tag is nested under the <Recipe> tag. There is nothing wrong with nesting XML tags, and it provides an intuitive representation of the data. However, translating nested elements into tables, rows, and columns can be somewhat complex.

You can, in fact, load the XML document in Listing 13.10 into a DataSet. However, the DataSet must do some extra work to represent the structure of the XML document using a relational view. The page in Listing 13.11 demonstrates how a DataSet would translate the Recipes.xml file.

Listing 13.11 ReadRecipes.aspx
<%@ Import Namespace="System.Data" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim dstRecipes As DataSet
 
  dstRecipes = New DataSet()
  dstRecipes.ReadXml( MapPath( "Recipes.xml" ) )
 
  lblTable1.Text = dstRecipes.Tables( 0 ).TableName
  dgrdTable1.DataSource = dstRecipes
  dgrdTable1.DataMember = dstRecipes.Tables( 0 ).TableName
 
  lblTable2.Text = dstRecipes.Tables( 1 ).TableName
  dgrdTable2.DataSource = dstRecipes
  dgrdTable2.DataMember = dstRecipes.Tables( 1 ).TableName
 
  lblTable3.Text = dstRecipes.Tables( 2 ).TableName
  dgrdTable3.DataSource = dstRecipes
  dgrdTable3.DataMember = dstRecipes.Tables( 2 ).TableName
 
  DataBind()
End Sub
 
</Script>
 
<html>
<head><title>ReadRecipes.aspx</title></head>
<body>
 
<asp:Label Font-Size="14pt" Runat="Server"/>
<br>
<asp:DataGrid CellPadding="10" Runat="Server" />
<p>
<asp:Label Font-Size="14pt" Runat="Server"/>
<br>
<asp:DataGrid CellPadding="10" Runat="Server" />
<p>
<asp:Label Font-Size="14pt" Runat="Server"/>
<br>
<asp:DataGrid CellPadding="10" Runat="Server" />
 
</body>
</html>

In the Page_Load subroutine in Listing 13.11, the Recipes.xml file is loaded into a DataSet. When the XML file is loaded, it is automatically converted into three tables: the Recipe, Ingredients, and Ingredient tables. The contents of each table are displayed by a DataGrid.

The Recipe table represents the name of each recipe and a recipe ID (the ID column is automatically generated for you). The Ingredient table represents each ingredient for a recipe. Finally, the Ingredients table joins the Recipe and Ingredient tables using the Recipe_ID and Ingredient_ID columns.

Instead of viewing the contents of the Recipes.xml document as a set of tables, you can view the contents of the XML document nonrelationally by using the XmlDataDocument class, which represents data as nodes on a tree. You create a new XmlDataDocument by using a DataSet in the XmlDataDocument‘s constructor.

In Listing 13.12, for example, an instance of the XmlDataDocument class is created from a DataSet. The XmlDataDocument then displays a list of all the ingredients in the Recipes.xml file.

Listing 13.12 XmlDataDocument.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Xml" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim dstRecipes As DataSet
  Dim objDataDocument As XmlDataDocument
  Dim objRecipes As XmlNodeList
  Dim objRecipe As XmlNode
 
  dstRecipes = New DataSet()
  dstRecipes.ReadXml( MapPath( "Recipes.xml" ) )
 
  objDataDocument = New XmlDataDocument( dstRecipes )
  objRecipes = objDataDocument.GetElementsByTagName( "Ingredient" )
  For Each objRecipe In objRecipes
    lblOutput.Text &= objRecipe.InnerXml
  Next
End Sub
 
</Script>
 
<html>
<head><title>XmlDataDocument.aspx</title></head>
<body>
 
<h2>All Ingredients</h2>
<asp:Label Runat="Server" />
 
</body>
</html>

NOTE

You must import the System.Xml namespace before using the XmlDataDocument class.

In the Page_Load subroutine in Listing 13.12, an instance of the XmlDataDocument class is created from a DataSet. Next, the GetElementsByTagName() method retrieves a list of nodes that have the tag name Ingredient. Finally, this list of nodes is displayed in a Label control. When the page is displayed, a combined list of ingredients for all the recipes is displayed.

Using the XmlDataDocument class with the DataSet class provides you with a great deal of flexibility. You can use these two classes to provide very different views on the same underlying data. If you want to represent data with tables, columns, and rows, you can use a DataSet. If you want to represent the same data as nodes on a tree, you can use the XmlDataDocument class.

Transforming XML with XSL Stylesheets

The eXtensible Stylesheet Language (XSL) enables you to perform transformations on the contents of an XML document. The .NET classes support the W3C XSL Transformations (XSLT) specification.

NOTE

To learn more details about XSL, visit the W3C Web site at http://www.w3.org/style/xsl.

XSL has several applications. One common use for XSL stylesheets is converting an XML representation of data into nicely formatted HTML that can be displayed in a browser. For example, you can take an XML document that represents a list of recipes and, by using different XSL stylesheets, display the same information in different ways. You might want to display only the recipes for breakfast foods, or you might want to display the recipes in a particular order.

Another use of XSL is for data format conversion. If you have an application that requires data to be organized with a particular format, you can use XSL to convert an XML document into the correct format for the application. In other words, XSL enables you to easily share information between different applications.

You can use XSL stylesheets from within an ASP.NET application in two ways: You can use the ASP.NET Xml control, or you can use the XSLTransform class. You explore both methods in the following sections.

Using the ASP.NET Xml Control

You can use the ASP.NET Xml control to simply display the raw contents of an XML document. Alternatively, you can use the control with an XSL stylesheet to format an XML document.

The Xml control supports the following four properties:

  • DocumentSource— The path to an XML file
  • TransformSource— The path to an XSL file
  • Document— A preloaded XML document
  • Transform— A preloaded XSL stylesheet

If you want to grab an XML file from the file system and transform the XML data with an XSL stylesheet, you can use the DocumentSource and TransformSource properties. For example, the page in Listing 13.13 uses the Xml control to format the contents of the Recipes.xml file.

Listing 13.13 XmlControl.aspx
<html>
<head><title>XMLControl.aspx</title></head>
<body>
 
<asp:Xml
  DocumentSource="recipes.xml"
  TransformSource="TransformRecipes.xsl"
  Runat="Server" />
 
</body>
</html>

The Xml control in Listing 13.13 uses an XSL stylesheet named TransformRecipes.xsl to format the contents of the recipes.xml file. The TransformRecipes.xsl file is contained in Listing 13.14.

Listing 13.14 TransformRecipes.xsl
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="Recipe">
    <h2><xsl:value-of select="Name" /></h2>
    <xsl:for-each select="Ingredients/Ingredient" >
      <li><xsl:value-of select="." /></li>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

In Listing 13.14, an XSL stylesheet uses XPath expressions to match elements in an XML file. When an element is matched, a template indicates how the element should be formatted.

NOTE

To learn more details about XPath, visit the W3C Web site at http://www.w3.org/tr/xpath.

In Listing 13.14, the first template matches Recipe elements. Whenever a Recipe element is found, the Name element is formatted with the HTML <H2> tag. Next, each ingredient for a particular recipe is formatted with HTML <LI> tags to create a bulleted list.

You can use the Xml control’s Document and Transform properties with preloaded XML documents and XSL stylesheets. For example, the page in Listing 13.15 uses the Xml control to format the contents of a DataSet ().

Listing 13.15 DataSetTransform.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Xml" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim conNorthwind As SqlConnection
  Dim dadProducts As SqlDataAdapter
  Dim dstProducts As DataSet
 
  conNorthwind = New SqlConnection( "Server=localhost;UID=sa;PWD=secret;Database=Northwind" )
  dadProducts = New SqlDataAdapter( "Select * From Products", conNorthwind )
  dstProducts = New DataSet()
  dadProducts.Fill( dstProducts, "Products" )
 
  xmlProducts.Document = New XmlDataDocument( dstProducts )
  xmlProducts.TransformSource = "TransformProducts.xsl"
End Sub
 
</Script>
 
<html>
<head><title>DataSetTransform.aspx</title></head>
<body>
 
<asp:Xml   Runat="Server" />
 
</body>
</html> 

In Listing 13.15, a DataSet is populated with the contents of the Products table from the Northwind database. An XmlDataDocument is created from the DataSet and assigned to the Document property of the Xml control. The Xml control uses an XSL stylesheet named TransformProducts.xsl to format the list of products in the DataSet.

The TransformProducts.xsl stylesheet is included in Listing 13.16.

Listing 13.16 TransformProducts.xsl
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
  <xsl:template match="Products">
    <p>
    <table width="100%" border="1"
      cellpadding="10" cellspacing="0">
      <tr>
        <td bgcolor="yellow">
          <b><xsl:value-of select="ProductName" /> </b>
        </td>
      </tr>
      <tr bgcolor="#eeeeee">
        <td>
          <b>Price:</b>
          $<xsl:value-of select="UnitPrice" />
        </td>
      </tr>
    </table>
    </p>
  </xsl:template>
</xsl:stylesheet>

The stylesheet in Listing 13.16 formats the products from the Products database table. It contains one template that matches all the Products elements. Each product is displayed as a separate HTML table. Within the HTML table, both the name and price of the product are displayed.

Using the XslTransform Class

Instead of using the ASP.NET Xml control to transform an XML document with an XSL stylesheet, you can use the XslTransform class. The advantage of using the XslTransform class is that it enables you to work with XSL transforms without necessarily displaying the results. For example, you can use the XslTransform class to transform an XML document and save it to the file system.

The page in Listing 13.17 uses the XslTransform class to apply the TransformProducts.xsl stylesheet to the Products table in the Northwind database. The results of the transformation are written directly to the Response object’s output stream.

Listing 13.17 Xsl.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Xml.Xsl" %>
 
<Script Runat="Server">
 
Sub Page_Load
  Dim conNorthwind As SqlConnection
  Dim dadProducts As SqlDataAdapter
  Dim dstProducts As DataSet
  Dim objDataDocument As XmlDataDocument
  Dim objXsl As XslTransform
 
  conNorthwind = New SqlConnection( "Server=localhost;UID=sa;PWD=secret; Database=Northwind" )
  dadProducts = New SqlDataAdapter( "Select * From Products", conNorthwind )
  dstProducts = New DataSet()
  dadProducts.Fill( dstProducts, "Products" )
 
  objDataDocument = New XmlDataDocument( dstProducts )
 
  objXsl = New XslTransform
  objXsl.Load( MapPath( "TransformProducts.xsl" ) )
  objXsl.Transform( objDataDocument, Nothing, Response.OutputStream )
End Sub
 
</Script>
 

In Listing 13.17, a DataSet is populated with the contents of the Products table from the Northwind database. Next, an instance of the XmlDataDocument class is created from the DataSet. The XmlDataDocument is used with the XslTransform class.

Next, an instance of the XslTransform class is created. The XslTransform‘s Load method retrieves an XSL stylesheet from the file system, and the Transform method is called. The Transform method converts the contents of the XmlDataDocument using the stylesheet and outputs the results to the Response object’s output stream.

Using Strongly Typed DataSets

Normally, when you work with a DataSet, you must work with several collections. To retrieve the value of a certain column in a certain row, you must work with the DataSet‘s Tables and Rows collections. For example, to display the value of the Title column for all the rows of the Titles table, you must write code that looks like this:

Dim drowDataRow As DataRow
For Each drowDataRow in dstDataSet.Tables( "Titles" ).Rows
  Response.Write( drowDataRow( "Title" ) )
Next 

This section examines an alternative way to access the information contained in a DataSet. Instead of accessing data in a DataSet by working with collections, you can access the data by using strongly typed methods and properties specified by an XML schema. In this section, you learn how to create a strongly typed DataSet.

The advantage of using a strongly typed DataSet is subtle, and doing so might not always be worth the effort. However, if you create a strongly typed DataSet, you can create more readable code. With a strongly typed DataSet, you can refer to tables and columns by name.

With a strongly typed DataSet, for example, you can display the value of the Title column for all the rows in the Titles table like this:

Dim drowTitleRow As TitlesRow
For Each drowTitleRow in objTitlesDS.Titles
  Response.Write (drowTitleRow.Title)
Next 

When you create a typed DataSet, you create a new class that represents the DataSet. In this example, you created a new class named objTitlesDS that represents the DataSet. After you create this class, you can retrieve a reference to the Titles DataTable by using the Titles property. Furthermore, you can refer to the value of the Title column by using the Title property.

Again, the difference here is not earth shattering. A strongly typed DataSet simply provides you with a slightly more intuitive means of referring to the members of a DataSet.

To create a strongly typed DataSet, you must complete each of the following steps:

  • Create an XML schema that represents the structure of the DataSet.
  • Use the XML schema with the Xsd.exe command-line tool to generate the source code for a class that represents the DataSet.
  • Compile the source code for the class and copy the compiled class into your application’s /bin directory.

Now, create a strongly typed DataSet that contains the Titles table from the Pubs database table. The first step is to create an XML schema that specifies the structure of the DataSet and Titles table. You could write this schema by hand; however, an easier way is to use the page in Listing 13.18. It contains an ASP.NET page that will automatically generate the proper XML schema for you.

Listing 13.18 GetXmlSchema.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Xml" %>
 
<Script Runat="Server">
 
Const strTableName As String = "Titles"
 
Sub Page_Load
  Dim conPubs As SqlConnection
  Dim dadDataAdapter As SqlDataAdapter
  Dim dstDataSet As DataSet
  Dim strXmlSchema As String
 
  conPubs = New SqlConnection("Server=localhost;UID=sa;PWD=secret; Database=Pubs" )
  dadDataAdapter=New SqlDataAdapter("Select * From " & strTableName, conPubs )
  dstDataSet = New DataSet()
  dstDataSet.DataSetName = strTableName & "DS"
  dadDataAdapter.Fill( dstDataSet, strTableName )
 
  strXmlSchema = Server.HTMLEncode( dstDataSet.GetXmlSchema() )
  Response.Write( "<pre>" & strXmlSchema & "</pre>" )
End Sub
 
</Script>

When you execute the page in Listing 13.18, the page displays the XML schema for the Titles table in the Pubs database. If you want to display the schema for another table, you can modify the strTableName constant and change the database connection string to refer to the proper database.

When you execute the page in Listing 13.18, the XML schema in Listing 13.19 is displayed.

Listing 13.19 TitlesDS.xsd
<xsd:schema targetNamespace="" xmlns=""
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xsd:element msdata:IsDataSet="true">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:element>
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
              <xsd:element minOccurs="0" />
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
 

If you look closely at the schema, you notice that the schema’s ID is the name of the DataSet (TitlesDS), and the schema includes a Titles element that represents the Titles database table.

Copy the schema generated by the GetXMLSchema.aspx page to a new file and save the file with the name TitlesDS.xsd. You need to use this schema when generating the source code for the strongly typed DataSet class.

The next step is to generate the source code for the class. You use the Schema Definition Tool (Xsd.exe) from the command line. Open a DOS prompt and change to the directory that contains the TitlesDS.xsd file. Next, execute the following statement from the command line:

Xsd.exe /d /l:vb TitlesDS.xsd

This statement generates the source for a new Visual Basic class file named TitlesDS.vb. The /d directive indicates that a DataSet should be generated, and the /l directive specifies the language to use for the class file.

If you are curious, go ahead and open the TitlesDS.vb file in Notepad. The class file contains methods and properties that correspond to the tables and columns contained in the TitlesDS DataSet.

NOTE

You can use the Xsd.exe tool to generate the class in a particular namespace. To do so, use the /n: directive and supply a namespace when generating the source for the class file.

Now that you have the source for the class file, you need to compile it. Execute the following statement from the command line:

vbc /t:library /r:System.dll,System.Data.dll,System.Xml.dll TitlesDS.vb
After you execute this command, a new file named TitlesDS.dll is created.

The final step is to copy the compiled class file to the application /bin directory. If the /bin directory does not exist at the root of your ASP.NET application, you can create it. Copying the compiled class to the /bin directory makes the class visible to all your ASP.NET pages. 

The page in Listing 13.20 illustrates how you can use the typed DataSet in an ASP.NET page. The page displays the values of the Title and Notes columns for all the rows in the Titles database table.

Listing 13.20 TypedDataSet.aspx
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<Script Runat="Server">
 
Sub Page_Load
  Dim conPubs As SqlConnection
  Dim dadTitles As SqlDataAdapter
  Dim objTitlesDS As TitlesDS
  Dim objTitleRow As TitlesDS.TitlesRow
 
  ' Fill the typed dataset
  conPubs = New SqlConnection( "Server=localhost;UID=sa;PWD=secret;Database=Pubs" )
  dadTitles = New SqlDataAdapter( "Select * From Titles", conPubs )
  objTitlesDS = New TitlesDS()
  dadTitles.Fill( objTitlesDS, "Titles" )
 
  ' Display its titles
  For each objTitleRow in objTitlesDS.Titles
    Response.Write( "<br>" & objTitleRow.Title )
    If Not objTitleRow.IsNotesNull Then
      Response.Write( "<br>" & objTitleRow.Notes )
    End If
    Response.Write( "<hr>" )
  Next
End Sub
 
</Script>

In Listing 13.20, the Fill() method fills the typed DataSet with all the rows from the Titles table. After the rows are added, a For...Each loop displays the values of the Titles and Notes columns for each row.

Notice that the Titles table is returned as a property of the DataSet. Also, notice that each column name is returned as a property of the TitlesRow object. Finally, IsNotesNull is a method of the typed DataSet. This method returns True when the Notes column has the value Null and False otherwise.

Advertisements

One Response to “Working with XML data in ASP.NET”

  1. Srini said

    Wonderful blog.. This is exactly what I was looking for this morning.

    Keep up great work.

    Srini

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: