asp:Feature
LANGUAGES: C#
ASP.NET VERSIONS: 2.0
New XML Features in .NET Version 2: Part II
Memory-based XML APIs
By Dan Wahlin
This is the second article in a series covering new XML features found in .NET version 2. If you didn t get a chance to read Part I, it focused on enhancements to the XmlReader and XmlWriter classes. Topics covered included performance enhancements, new XmlReader properties and methods that simplify XML parsing, new ways to generate XML data with XmlWriter, and new supporting classes such as XmlReaderSettings and XmlWriterSettings. If you re writing an application that consumes or generates XML data and requires the most scalable and efficient APIs available, you can t go wrong with the XmlReader and XmlWriter classes.
In this second article I m going to shift gears and focus on the memory-based XML APIs available in version 2 of the .NET Framework. This article will show enhancements to the XmlDocument and XPathNavigator classes, as well as provide examples of using the new XslCompiledTransform class. Let s start by taking a look at new functionality in the XmlDocument class.
New Features in the XmlDocument Class
The XmlDocument class provides an easy to use set of properties and methods for loading XML data into memory and manipulating or querying it. Because XmlDocument is based on the W3C Document Object Model (DOM) specification, many people with DOM experience are comfortable using it. However, developers accustomed to working with Microsoft s MSXML parser (the predecessor to the XML parsers in the .NET Framework) missed the ability to validate a DOM structure against a schema. In .NET 1.1 this type of validation could be performed using the XmlValidatingReader but it was somewhat cumbersome. With the release of .NET version 2, the XmlDocument class now exposes a validation API that makes it trivial to validate XML against XSD schemas.
Validation can be performed using the new Validate method combined with the new Schemas property. Figure 1 shows how these new additions can be used together.
public void btnSubmit_Click(object sender, EventArgs e) {
//Load schema used to validate
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(String.Empty, schemaPath);
schemaSet.Compile();
//Validate XML using an XmlDocument's Validate() method
XmlDocument doc = new XmlDocument();
doc.Load(xmlPath);
doc.Schemas = schemaSet; //Assign schema
doc.Validate(
new ValidationEventHandler(doc_ValidationEventHandler));
this.lblOutput.Text = (status) ? "Validation Succeeded!" :
"Validation Failed!";
}
void doc_ValidationEventHandler(object sender, ValidationEventArgs e) {
//Errors could be logged or written out to the application here
status = false;
}
Figure 1: Validating XML data loaded into a DOM structure has been greatly simplified with the XmlDocument class available in .NET version 2. This example shows how the Validate method and Schemas property can be used to perform validation.
In addition to showing how the Schemas and Validate members can be used, the code in Figure 1 also demonstrates using the new XmlSchemaSet class. XmlSchemaSet replaces the XmlSchemaCollection class available in .NET 1.1. XmlSchemaSet works much like XmlSchemaCollection as far as adding schemas into the schema collection, but it provides complete control over when the schemas are actually compiled. This is made possible by the new Compile method.
Once a schema is loaded, the XmlSchemaSet object is assigned to the XmlDocument object s Schemas property. The Validate method is then called, which points any validation errors that are encountered to an event handler named doc_ValidationEventArgs that performs the function of assigning a variable named status to false. doc_ValidationEventArgs could also be used to write validation errors to the user interface or to a log file, as needed.
New Features in the XPathNavigator Class
The XmlDocument class isn t the only memory-based API available in the .NET Framework. In version 2, the XPathNavigator class can also be used to perform XPath queries, validate XML against XSD schemas, and insert, update, and delete nodes. What s the difference between the two? In a nutshell, the XmlDocument class is based on the W3C DOM specification, whereas the XPathNavigator is Microsoft s custom implementation and recommended API for working with XML data that is loaded into memory. Although there is much the XPathNavigator can do, this section will focus on new features in version 2 that allow you to edit, validate, and query XML data.
Editing XML data using the XPathNavigator class can be accomplished by using the new SetValue, AppendChild, and DeleteSelf methods. Figure 2 shows an example of using these methods to modify a simple XML document (see Figure 3) containing Web links.
//mode will be "Insert", "Edit" or "Delete"
private void EditXml(string mode, string id) {
XmlDocument doc = new XmlDocument();
doc.Load(_XmlPath);
XPathNavigator editor = doc.CreateNavigator();
editor.MoveToFirstChild();
//Get to
//Find a node based upon the id attribute
XPathNavigator node = editor.SelectSingleNode("link[@id='" +
id + "']");
if (node != null) {
//Found node so move to it
editor.MoveTo(node);
switch (mode) {
case "Edit":
editor.MoveToAttribute("name", String.Empty);
editor.SetValue(this.txtName.Text);
editor.MoveToParent();
editor.MoveToAttribute("href", String.Empty);
editor.SetValue(this.txtURL.Text);
break;
case "Delete":
editor.DeleteSelf();
break;
}
}
else {
//No node found
if (mode == "Insert") {
using (XmlWriter writer = editor.AppendChild()) {
writer.WriteStartElement("link");
writer.WriteAttributeString("id",
Guid.NewGuid().ToString());
writer.WriteAttributeString("name", this.txtName.Text);
writer.WriteAttributeString("href", this.txtURL.Text);
writer.WriteEndElement();
}
}
}
//Save() will fail if ASPNET account doesn't have write access
doc.Save(_XmlPath);
}
Figure 2: The XPathNavigator now supports editing XML data.
name="ASP.NET"
href="http://msdn.microsoft.com/asp.net" />
name=".NET Framework"
href="http://msdn.microsoft.com/netframework" />
name="XML"
href="http://msdn.microsoft.com/xml" />
name="Web Services"
href="http://msdn.microsoft.com/webservices" />
Figure 3: The links XML document modified by the XPathNavigator shown in Figure 2.
Looking through the code in Figure 2 you ll see that an XPathNavigator instance is created using the XmlDocument class CreateNavigator method. Any class that implements the IXPathNavigable interface can create an XPathNavigator instance (XPathDocument, XmlNode, and XmlDocument all implement this interface). Once the XPathNavigator instance is created, a call to the MoveToFirstChild method is made to move the navigator to the root node of the document.
Once the root node is accessed an XPath query is executed using SelectSingleNode that attempts to find a specific node in the XML document based on the value of the node s id attribute. If the node is found, a delete or edit operation is occurring. If the node isn t found, then an insert operation is occurring. To edit a node use the XPathNavigator s SetValue method; to delete a node use the DeleteSelf method.
Performing inserts to an XML document using XPathNavigator is quite different compared to using the XmlDocument class. With XmlDocument you call various create methods, such as CreateElement and CreateTextNode, to add new nodes to the XML document. To create a node using XPathNavigator you call the new AppendChild method. AppendChild returns an XmlWriter instance that can be used to create elements, attributes, text nodes, and so forth. The example shown in Figure 2 creates an element named link and three attributes named id, name, and href. The using keyword wrapped around the AppendChild call is a C# construct that ensures that the XmlWriter is closed as soon as it s done.
Once XML data has been inserted, deleted, or edited you ll need to save the data to a file or other type of data store. The XPathNavigator doesn t have a save method, but changes made in memory are automatically captured by the XmlDocument object that was used to create the XPathNavigator. As a result, saving the XML data is as simple as calling the XmlDocument s Save method.
The XPathNavigator can also be used to validate XML data in version 2. To validate an XML document you first load one or more schemas into an XmlSchemaSet collection. You then call the XPathNavigator s CheckValidity method, which takes XmlSchemaSet and ValidationEventHandler objects as arguments. ValidationEventHandler is a delegate that identifies which method to call if validation errors occur that need to be logged to a file or reported back to the user interface. CheckValidity returns a Boolean true if the validation succeeds. Figure 4 shows the overall process for validating an XML document against an XSD schema using the XPathNavigator.
public void btnSubmit_Click(object sender, EventArgs e) {
//Load schema used to validate
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(String.Empty, schemaPath);
schemaSet.Compile();
//Validate XML using an XPathNavigator's CheckValidity() method
XPathDocument doc = new XPathDocument(xmlPath);
XPathNavigator nav = doc.CreateNavigator();
bool status = nav.CheckValidity(schemaSet,
new ValidationEventHandler(nav_ValidationEventHandler));
this.lblOutput.Text = (status) ? "Validation Succeeded!" :
"Validation Failed!";
}
void nav_ValidationEventHandler(object sender, ValidationEventArgs e) {
//Errors could be logged here
}
Figure 4: Validating an XML document against an XSD schema using the XPathNavigator.
The final XPathNavigator topic I ll discuss is a feature that can definitely save you hours of time wondering why your code doesn t work as expected. XML namespaces can make it difficult to successfully use XPath statements if the namespaces aren t taken into consideration. To execute an XPath query with namespaces in version 1.1 using a navigator you had to create XPathNavigator, XPathExpression, and XmlNamespaceManager objects to successfully locate namespace-qualified nodes in the XML document. This process was cumbersome and quite frustrating to developers new to XML.
In version 2 the process has been simplified. Take for
example an XML document that has a node with a namespace defined, such as this
http://www.xmlforasp.net
You can now access the node using an XPath statement
without creating additional objects to deal with the local namespace. For
example, accessing all the
string xmlPath = Server.MapPath("~/XML/MSDN.xml");
XPathDocument doc = new XPathDocument(xmlPath);
XPathNavigator nav = doc.CreateNavigator();
nav.MoveToFirstChild();
foreach (XPathNavigator node in
nav.Select("channel/item/dc:creator",nav)) {
//Process creator node data
}
Notice that the dc namespace prefix is added directly into the XPath statement but is not defined anywhere else in the code. The Select method will automatically handle resolving the namespace prefix to the namespace URI (http://purl.org/dc/elements/1.1/) by reading the namespaces loaded in the XPathNavigator instance (the navigator instance is passed as the second parameter to the Select method). This is a huge productivity enhancement that greatly simplifies accessing namespace-qualified nodes in an XML document. This functionality is made possible by a new interface named IXmlNamespaceResolver that XPathNavigator implements in version 2.
Performing XSLT Transformations
XSLT provides a great way to transform XML data into a variety of formats, such as HTML, WML, and even other forms of XML. Transformations in version 1.1 were performed using the XslTransform class located in the System.Xml.Xsl namespace. XslTransform performed adequately, but never matched up with the XSLT performance found in MSXML (Microsoft s COM-based XML parser with XSLT capabilities). In .NET version 2, transformation speeds have increased significantly because of a new class named XslCompiledTransform.
From an API standpoint, XslCompiledTransform looks much like the XslTransform class of old. However, it compiles XSLT stylesheets into intermediate code (just as C#, VB.NET, and other languages are compiled) before performing an XSLT transformation resulting in significant performance gains. A stylesheet can be cached and re-used for other transformations once it has been compiled. Figure 5 shows an example of using the XslCompiledTransform class to transform an RSS feed into HTML. If you ve used the XslTransform class, you ll notice that the XslCompiledTransform class exposes the same methods.
//Write output of transformation to an XmlWriter
//which writes to a StringBuilder
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
//Load the XML data that will be transformed
XPathDocument doc =
new XPathDocument(Server.MapPath("~/Xml/MSDN.xml"));
//Perform XSLT Transformation
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load(Server.MapPath("MSDN.xslt"));
trans.Transform(doc, null, writer);
//Close the XmlWriter and write the transformation output
//to a Label in an ASP.NET Web Form
writer.Close();
this.lblOutput.Text = sb.ToString();
Figure 5: The XslCompiledTransform class replaces the XslTransform class found in version 1.1 of the framework.
The code in Figure 5 starts by creating XmlWriter and StringBuilder objects that are written to during the transformation process. It then loads the source XML document into an XPathDocument object. Next, an XslCompiledTransform object is created and loaded with an XSLT stylesheet named MSDN.xslt. Finally, the transformation is performed by calling the Transform method and the output is written to an ASP.NET Label control. The HTML output generated is shown in Figure 6.
Figure 6: The HTML output generated
by the XslCompiledTransform class.
Conclusion
In this second article covering new XML features in .NET version 2 you ve been introduced to enhancements made to classes that work with XML data loaded into memory. While the XmlDocument class hasn t changed much from version 1.1, it does offer a way to validate XML documents directly. The XPathNavigator has been significantly overhauled and now offers insert, update, and delete capabilities, as well as support for validation against XSD schemas. Finally, the XslCompiledTransform class provides a significant boost to performance compared to its XslTransform predecessor.
In Part III I ll demonstrate how several new version 2 classes can be used to bind XML data to a variety of ASP.NET server controls. Until then, happy coding!
The sample code accompanying this article is available for download.
Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web services) is president of Wahlin Consulting, as well as a .NET instructor at Interface Technical Training. Dan founded the XML for ASP.NET Developers Web site (http://www.XMLforASP.NET), which focuses on using XML, ADO.NET, and Web services in Microsoft s .NET platform. He s also on the INETA Speaker s Bureau and speaks to .NET User Groups around the US. Dan co-authored Professional Windows DNA (Wrox), ASP.NET: Tips, Tutorials, and Code (SAMS), and ASP.NET 1.1 Insider Solutions (SAMS), and authored XML for ASP.NET Developers (SAMS).