13.8 Working with XML Namespaces

 <  Day Day Up  >  

The XML document that your application is parsing contains namespaces that you must resolve.


Technique

When you load an XML file containing namespace declarations and references using the XmlDocument class, you'll notice something peculiar: nothing. Everything is working just as you want with no exceptions or errors being returned ”that is, until you decide to call the SelectSingleNode method to retrieve an XmlNode object. If it occurs and the node you are attempting to retrieve contains a namespace prefix, an exception is thrown. This exception occurs because the XML readers do not automatically resolve namespaces for you. It's something you have to do yourself.

The SelectSingleNode contains an additional overload that uses an XmlNamespaceManager object. This object is used by the XmlDocument class to resolve any namespace prefixes it encounters when asked to navigate the document using XPath expressions. Within this class is a table called the NameTable . Its purpose is to simply associate a namespace prefix with its corresponding URI. The table itself, however, isn't created by simply parsing an XML document. You have to manually add the namespace prefixes and URIs when the document is being parsed.

If you are already familiar with the namespaces that will be used within an XML document and you don't expect the prefixes for those namespaces to change, you can just call the AddNamespace method defined in the XmlNamespaceManager class. For instance, after you load an XML file into an XmlDocument object, create an XmlNamespaceManager object, associate it with the XmlDocument , and then call the AddNamespace method:

 
 XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load( "books.xml" ); XmlNamespaceManager xmlNS = new XmlNamespaceManager( xmlDoc.NameTable ); xmlNS.AddNamespace( "book", "urn:test" ); XmlNode node = xmlDoc.SelectSingleNode( "//book:info[@title='A Book Title']" ); 

Whenever an XML node is queried for using SelectSingleNode and it contains the "book" namespace prefix, the XmlDocument object is able to successfully resolve the URI of the namespace prefix by accessing the NameTable in the XmlNamespaceManager .

There might be instances where your application isn't tied to a specific schema but instead works with any XML documents, as is the case with XML editors. In these instances, you have to build the NameTable as the file is being parsed. One possible solution is to do a substring check on any attributes of elements that are encountered . The substring to check for is "xmlns:" , which indicates that the attribute is a namespace declaration. The following code is a fragment of code from the project accompanying this chapter:

 
 while( rdr.Read() ) {     switch (xmlVR.NodeType)     {         // new start element found         case XmlNodeType.Element:         {             // populate attribute hashtable for element             if( xmlVR.HasAttributes == true )             {                 while( xmlVR.MoveToNextAttribute() )                 {                     if( xmlVR.Name.StartsWith( "xmlns:" ))                     {                         namespaceTable.AddNamespace(                             xmlVR.Name.Substring( 6 ), xmlVR.Value );                     }                 }             }         break;         }     } } 

Another possible solution, which doesn't require parsing the entire file, is to use an XPathNavigator to enumerate the namespaces contained within an XmlDocument . The XPathNavigator contains two enumeration methods named MoveToFirstNamespace and MoveToNextNamespace , allowing you to quickly jump to each namespace without having to parse through the individual elements and determine whether they contain a namespace prefix or not. You can easily place the following code into an application and return an XmlNamespaceManager that you can use in XPath query methods:

 
 private XmlNamespaceManager FillNamespaceTable( XmlDocument xmlDoc ) {     XmlNamespaceManager xmlNS = new XmlNamespaceManager(xmlDoc.NameTable);     XPathNavigator xPath = xmlDoc.DocumentElement.CreateNavigator();     if (xPath.MoveToFirstNamespace())     {         do         {             if ( xPath.Name.StartsWith("xml") == false )             {                 xmlNS.AddNamespace(xPath.Name, xPath.Value);             }         } while (xPath.MoveToNextNamespace());     }     return xmlNS; } 

Comments

Namespaces were created to avoid ambiguity. If two different developers are writing an application for a pet store, and both applications use a data layer consisting of XML files, the possibility that they will create the same element names is quite high. Furthermore, even if the elements do refer to the same physical entity, the semantics might still differ . In other words, if both developers used an element named "price" referring to the price of an object, one might use retail while the other is referring to wholesale price. This little scenario, of course, doesn't mention the probability that one developer's XML file will be sent to the other developer's application, but if they use namespaces, it wouldn't matter.

You might find it surprising that you have to put so much work into supporting XML namespaces into your application. You might even believe the assumption that the .NET classes should contain that functionality, freeing you from the hassle (albeit small hassle) of working with namespaces. The reasoning behind this assumption take us to the W3C recommendation.

XML namespaces are not identifiers for elements. They are merely placed for the convenience of the developer parsing the data to disambiguate element names. In fact, it's entirely legal to declare a namespace prefix twice in the same document but give them different URIs, as shown in the following example:

 
 <item:book xmlns:prefix="http://www.samspublishing.com"/> <item:book xmlns:prefix="http://www.anotherpublisher.com"/> 

When an XPath expression is created to select an item:book node, the XPath processor will be unable to determine which element it refers to. However, because you are more intimately familiar with the file format that your application consumes, you can use the XmlNamespaceManager to resolve the ambiguities at runtime for the benefit of the XPath processor. In this case, one solution is to change the prefix of one of the namespaces so a collision doesn't occur by specifying a different key in the call to AddNamespace :

 
 xmlNS.AddNamespace( "samsitem:book", "http://www.samspublishing.com" ); 
 <  Day Day Up  >  


Microsoft Visual C# .Net 2003
Microsoft Visual C *. NET 2003 development skills Daquan
ISBN: 7508427505
EAN: 2147483647
Year: 2003
Pages: 440

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