Dot Net Solutions
George V Place,
4 Thames Avenue
Windsor
Berkshire
SL4 1QP
Great Britain
0845 402 1752
GEO: -0.606174, 51.4843
 
 
 
 

Working with namespaces in LINQ to XML 

Tags: xml, azure, linq

I was working on an Azure utility with Johan on Friday, which had to deal with the XML responses provided by the Service Management REST API. XML like this:

<HostedServices xmlns="http://schemas.microsoft.com/windowsazure"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<HostedService>
<Url>https://management.core.windows.net/GUID/services/hostedservices/foo</Url>
<ServiceName>foo</ServiceName>
</HostedService>
</HostedServices>

 

Pop quiz: what would break if they left that xmlns attribute out of the root element?

Answer: Nothing. Nothing at all. XML namespaces are just used when you've got two or more schemata in a single XML document and the same symbol name might be defined by in more than one of them. Maybe, as in Tim Bray's original post on the subject, you're mixing book data with HTML and both data structures involve titles.

Because it's there…

It seems to be required in some circles – Microsoft included – to add an xmlns declaration in any and all XML, regardless of how small or self-contained it may be. And I suppose that, in and of itself, wouldn't really annoy me that much. But, try and process the XML above using Microsoft's own LINQ to XML classes (.NET 3.5 and above) and will it work?

Console.WriteLine("Starting...");
foreach
(var element in xml.Elements("HostedService"))
{
Console.WriteLine(element.Element("ServiceName"));
}
Starting...
Press any key to continue...

No.

Why not?

Because all these element names are actually part of a custom namespace, and LINQ to XML honours these namespaces most particularly.

So, how to make this code work? (You'll like this.)

foreach (var element in xml.Elements(xml.GetDefaultNamespace() + "HostedService"))
{
Console.WriteLine(element.Element(element.GetDefaultNamespace() + "ServiceName").Value);
}

I'm not even joking. The default namespace for all children of an element can be obtained by calling GetDefaultNamespace on the element. Adding this to the start of a string returns a namespace-qualified XName which will then resolve correctly. (Don't get me started on what the LINQ to XML team did with operator-overloading; I don't know if it was strictly necessary for the LINQ side of it, but it's an unintuitive, undiscoverable pain in the neck as far as I'm concerned.)

Just to be absolutely clear about this: there is no way to have an element with an XML element that is not either in the default namespace or in an explicitly specified namespace.

So WHY doesn't LINQ to XML just apply the default namespace automatically? I'd guess it's because by the time the XElement method gets the value you're supplying as a string, it's been converted into an XName by the implicit conversion operator, which has no knowledge of the context.

Quick fix

Anyway, it's simple enough to get around once you realise what's amiss:

public static class XElementExtensions
{
public static XElement GetElement(this XElement element, string name)
{
return element.Element(element.GetDefaultNamespace() + name);
}

public static IEnumerable<XElement> GetElements(this XElement element, string name)
{
return element.Elements(element.GetDefaultNamespace() + name);
}
}

Unfortunately, the compiler honours the implicit conversion from string to XName before it honours extension methods, otherwise I could call these methods Element() and Elements() and everything would just work. Never mind.

HTH.

Published: 17 Feb 2010  09:11
1  Comment  |  Trackback Url  | 0  Links to this post | Bookmark this post with:        
 
 
 
 

Links to this post

No linkbacks added
 
 
 
 

Comments

No comments added yet
 
 
 
 

Post comment

Name *:
URL:
Email:
Comments:


CAPTCHA Image Validation


 
 
 
 

Related posts