Jesper Tverskov, July 8, 2008

Read XML with XPathDocument

In ASP.NET we have four main APIs for reading XML. We can use the new LINQ to XML, the XmlReader, the XPathDocument and the XmlDocument classes. In this tutorial we look at XmlReader. The idea is only to show a basic example for comparison.

1. Read products.xml

I use the following XML document, products.xml, in many of my XML tutorials. How could it be transformed into an XHTML table using XPathDocument? To make the example more interesting, we use CSS for colors, etc.

The idea is only to show a basic example of how to use XPathDocument. There are many more properties and methods and ways of achieving the same result than indicated below. You must look up the proper documentation for all the details.

2. XPathDocument

2.1 read-xml-xpathdocument.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="read-xml-xpathdocument.aspx.cs" Inherits="read_xml_xpathdocument" %><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Read XML with XPathDocument</title>
    <style type="text/css">table, td, th{border:1px solid silver; border-collapse:collapse}th{background-color:mistyrose}tr.alt1{background-color: ivory}tr.alt0{background-color: azure}</style>
  </head>
  <body>
    <h1>Using XPathDocument</h1>
    <div id="div1" runat="server" />
  </body>
</html>

2.2 read-xml-xpathdocument.aspx.cs  

using System;
using System.IO;using System.Xml;
using System.Xml.XPath;

public partial class read_xml_xpathdocument : System.Web.UI.Page
{
  int counter;
  string tr;

  protected void Page_Load(object sender, EventArgs e)
  {
    string productsFile = Path.Combine(Request.PhysicalApplicationPath, "products.xml");

    XPathDocument xmldoc = new XPathDocument(productsFile);
    XPathNavigator nav = xmldoc.CreateNavigator();

    foreach (XPathNavigator product in nav.Select("products/product"))
    {
    counter++;
    tr += "<tr class='alt" + (counter % 2) + "'><td>" + product.SelectSingleNode("@id").Value + "</td><td>" + product.SelectSingleNode("name").Value + "</td><td>" + product.SelectSingleNode("price").Value + "</td><td>" + product.SelectSingleNode("stock").Value + "</td><td>" + product.SelectSingleNode("country").Value + "</td></tr>";
    }
    string th = "<th>id</th><th>name</th><th>price</th><th>stock</th><th>country</th>";
div1.InnerHtml = "<table cellspacing='0' cellpadding='5'>" + th + tr + "</table>";
  }
}

The code examples for XPathDocument and XmlDocument can be made almost identical if we also use XPathNavigator together with XmlDocument except that we use XPathDocument() to load XML when XPathDocument is used and load() to load XML when XmlDocument is used.

3. Read XML in a namespace

To get to XML in a namespace in a loaded XML hierarchy, we must also declare the same namespace in the code. In XPathDocument and in XmlDocument (W3C DOM) we need to use the XmlNamespaceManager. In XPath 1.0 we can only get to a namespace using a prefix. For that reason we need to make up some "dummy" prefix if we want to get to XML in a default namespace. Below we have chosen "ns".

The markup in products-ns.xml uses the following default namespace: xmlns="http://www.xmlplease.com''. To get to it we need to modify the code from our previous example like this:

XPathDocument xmldoc = new XPathDocument(productsFile);
XPathNavigator nav = xmldoc.CreateNavigator();

XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("ns", "http://www.xmlplease.com");
// we always need a prefix even for a default namespace

foreach (XPathNavigator product in nav.Select("ns:products/ns:product", nsMgr))
{
  counter++;
  tr += "<tr class='alt" + (counter % 2) + "'><td>" + product.SelectSingleNode("@id").Value + "</td><td>" + product.SelectSingleNode("ns:name", nsMgr).Value + "</td><td>" + product.SelectSingleNode("ns:price", nsMgr).Value + "</td><td>" + product.SelectSingleNode("ns:stock", nsMgr).Value + "</td><td>" + product.SelectSingleNode("ns:country", nsMgr).Value + "</td></tr>";}

Note that an attribute does not inherit the namespace of its parent element. That is: an attribute without a proper namespace prefix is never in a namespace. That is: attributes in elements in a default namespace are not in a namespace! See my article: Attributes and XML namespaces.

Updated 2008-07-12