Use Linq to Xml with Xml namespaces

C#Linq to-XmlXml Namespaces

C# Problem Overview


I have this code :

/*string theXml =
@"<Response xmlns=""http://myvalue.com""><Result xmlns:a=""http://schemas.datacontract.org/2004/07/My.Namespace"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><a:TheBool>true</a:TheBool><a:TheId>1</a:TheId></Result></Response>";*/

string theXml = @"<Response><Result><TheBool>true</TheBool><TheId>1</TheId></Result></Response>";

XDocument xmlElements = XDocument.Parse(theXml);

var elements = from data in xmlElements.Descendants("Result")
               select new {
                            TheBool = (bool)data.Element("TheBool"),
                            TheId = (int)data.Element("TheId"),
                          };

foreach (var element in elements)
{
    Console.WriteLine(element.TheBool);
    Console.WriteLine(element.TheId);
}

When I use the first value for theXml, the result is null, whereas with the second one, I have good values ...

How to use Linq to Xml with xmlns values ?

C# Solutions


Solution 1 - C#

LINQ to XML methods like Descendants and Element take an XName as an argument. There is a conversion from string to XName that is happening automatically for you. You can fix this by adding an XNamespace before the strings in your Descendants and Element calls. Watch out because you have 2 different namespaces at work.


string theXml =
@"http://myvalue.com""><Result xmlns:a=""http://schemas.datacontract.org/2004/07/My.Namespace"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><a:TheBool>true</a:TheBool><a:TheId>1</a:TheId></Result></Response>";;



        //string theXml = @"<Response><Result><TheBool>true</TheBool><TheId>1</TheId></Result></Response>";

XDocument xmlElements = XDocument.Parse( theXml );
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result" )
      select new
             {
                 TheBool = (bool) data.Element( nsa + "TheBool" ),
                 TheId = (int) data.Element( nsa + "TheId" ),
             };

foreach ( var element in elements )
{
    Console.WriteLine( element.TheBool );
    Console.WriteLine( element.TheId );
}





Notice the use of ns in Descendants and nsa in Elements

Solution 2 - C#

You can pass an XName with a namespace to Descendants() and Element(). When you pass a string to Descendants(), it is implicitly converted to an XName with no namespace.

To create a XName in a namespace, you create a XNamespace and concatenate it to the element local-name (a string).

XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";

var elements = from data in xmlElements.Descendants( ns + "Result")
                   select new
                              {
                                  TheBool = (bool)data.Element( nsa + "TheBool"),
                                  TheId = (int)data.Element( nsa + "TheId"),
                              };

There is also a shorthand form for creating a XName with a namespace via implicit conversion from string.

var elements = from data in xmlElements.Descendants("{http://myvalue.com}Result")
                   select new
                              {
                                  TheBool = (bool)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheBool"),
                                  TheId = (int)data.Element("{http://schemas.datacontract.org/2004/07/My.Namespace}TheId"),
                              };

Alternatively, you could query against XElement.Name.LocalName.

var elements = from data in xmlElements.Descendants()
                   where data.Name.LocalName == "Result"

Solution 3 - C#

I have several namespaces listed at the top of an XML document, I don't really care about which elements are from which namespace. I just want to get the elements by their names. I've written this extension method.

	/// <summary>
	/// A list of XElement descendent elements with the supplied local name (ignoring any namespace), or null if the element is not found.
	/// </summary>
	public static IEnumerable<XElement> FindDescendants(this XElement likeThis, string elementName) {
		var result = likeThis.Descendants().Where(ele=>ele.Name.LocalName==elementName);
		return result;
	}

Solution 4 - C#

I found the following code to work fine for reading attributes with namespaces in VB.NET:

MyXElement.Attribute(MyXElement.GetNamespaceOfPrefix("YOUR_NAMESPACE_HERE") + "YOUR_ATTRIB_NAME")

Hope this helps someone down the road.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTimView Question on Stackoverflow
Solution 1 - C#Mike TwoView Answer on Stackoverflow
Solution 2 - C#Lachlan RocheView Answer on Stackoverflow
Solution 3 - C#mike nelsonView Answer on Stackoverflow
Solution 4 - C#dotNETView Answer on Stackoverflow