Watch those Namespaces!

This will be a short post on a common problem to watch out for while parsing XML in the Silverlight Windows Phone 7 world. Just hoping to save someone ½ hour of looking around! Now, the Silverlight run-time on a Windows Phone is based on the .NET compact framework; so you don’t have access to the complete .NET framework & this is for good reasons. There are some things that one just should not do on a smaller device (like synchronous operations that lock up the UI thread); also, the compact framework has been optimized to provide for the special needs of a touch-based form factor.

So, let’s say you are writing an application that consumes an Atom/XML/RSS feed. The “WebClient” or “HTTPWebRequest” classes are what most WP7 developers use to make a web request; although the latter has proven to be better performing (as explained here). The core idea is to fire off an HTTP Get, after assigning an event handler to process the response asynchronously. So, I had the usual code as below:


   WebClient FeedGetter = new WebClient();
   FeedGetter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Feed_Downloaded);
   FeedGetter.DownloadStringAsync(new Uri("some URI"));

Now, once the response was available, it was a matter of parsing the XML feed. Since we do not have the luxury of XMLDocument in the Silverlight framework, the next best alternative is to use XDocument to build the XML tree for parsing & then use LINQ to XML to hydrate needed objects. This actually works outs well since LINQ has been shown to performing better than XPath node processing. So, again, I had the usual code in place:


 private void Feed_Downloaded(object sender, DownloadStringCompletedEventArgs e)
 {
    if (e.Error != null)
    {
        // Do something
    }
    else
    {
       // Grab response.
       XDocument xmlFeed = XDocument.Load(new StringReader(e.Result));
                
       // LINQ to parse.
       var someCollection = from xElem in xmlFeed.Elements("SomeNode")
                                          select new someObj
                                           {
                                                Property1 = xElem.Element("childnode1").Value,
                                                Property2 = xElem.Element("childnode2").Value
                                           };

        // Loop through someCollection & stuff.
      }
  }

But, nothing happened! No errors in parsing; yet my objects were null. A little investigation revealed that the LINQ parsing was not working as the “Elements” node collection came back empty! Hmmm .. nothing unusual in code. So, what was causing the failure? Turns out, the XML feed & XDocument were both responsible.

The XML looked like this:


 <Root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="someNamespace">
  <childnode1 .... />
  <childnode2 .... />
  ..
  ..
 </Root>

Turns out, since the root node names a “Namespace” explicitly, every node in the tree belongs to that Namespace! And the XDocument method needs the fully qualified XName node name to uniquely find the nodes in the tree. So, the following is the workaround. For repeated use, you might want to use a constant.


  XElement feedNode = xmlFeed.Element("{someNamespace}Root");

Or something like this:


  XName qualifiedNodeName = XName.Get("Root", "someNamespace");
  XElement[] childNodes = xmlFeed.Descendants(qualifiedNodeName);

As an afterthought, while parsing XML through LINQ to hydrate your objects definitely works, it may not be optimal. Recently, on a friend’s advice, I have started using the .NET Serialization classes to do this . Turns out, data annotations, when correctly used are very powerful while deserializing XML/Atom feeds. May be a later post to explain what I mean.

Till then,
Adios!

Advertisement

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 )

Facebook photo

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

Connecting to %s