Working with XML Namespaces and LINQ
October 11th 2010 | Ben Yoder
Working with XML Namespaces and LINQ
While working on a project using LINQ to XML to parse a web response, I ran into an issue whereby my calls to get an entity’s Descendants or Elements were returning no results. It turned out that the specified response included a namespace. In this case you can’t use the common element name—you must get the fully qualified XName.
For example, given the following XML and LINQ Query:
string xml = @"<Employees> <Employee> <FirstName>John</FirstName> <LastName>Smith</LastName> <Id>1</Id> </Employee> </Employees>"; XElement employee = XElement.Parse(xml); var employees = from e in employee.Descendants("Employee") select new { Id = e.Element("Id").Value, FirstName = e.Element("FirstName").Value, LastName = e.Element("LastName").Value };
The query works and the employees object contains John Smith. However if the XML changes, the query returns nothing:
<h:Employees xmlns:h="http://www.w3.org">
<h:Employee>
<h:FirstName>John</h:FirstName>
<h:LastName>Smith</h:LastName>
<h:Id>1</h:Id>
</h:Employee>
</h:Employees>
After browsing through some MSDN documentation, I learned that I have to include the namespace in the query. I got the full name using XName.Get()
XName root = XName.Get("Employee", "http://www.w3.org"); XName firstName = XName.Get("FirstName", "http://www.w3.org"); XName lastName = XName.Get("LastName", "http://www.w3.org"); XName id = XName.Get("Id", "http://www.w3.org");
Replace the names in the LINQ query and it returns the correct results. Of course, there are other ways to accomplish this. You could create an XNamespace object based on the namespace string and prepend it to each element name in the query. Creating an individual XName object for each query parameter gets kind of verbose, so for more complex LINQ this is probably not the preferred way.
The simplest option would probably be to get the XNamespace from the element. This would work with or without a declared namespace without having to hard-code the namespace URI.
Also, it’s worth remembering that “e.Element(name).Value” will throw an exception if the element doesn’t exist; “(string)e.Element(name)” will simply return null.
var ns = employee.Name.Namespace;
var employees = from e in employee.Descendants(ns + “Employee”)
select new
{
Id = (string)e.Element(ns + “Id”),
FirstName = (string)e.Element(ns + “FirstName”),
LastName = (string)e.Element(ns + “LastName”),
};
Agreed, the XNamespace is a much cleaner way to go. As I mentioned, the individual XNames get out of hand with larger queries. Thanks for the tip and example regarding ‘e.Element(name).Value’, I was unaware of that distinction.
If you are struggling with xml namespaces, there is a great tutorial on xpath namespaces at xml reports. It walks you through it in very simple steps
xml reports