Use Linq to Xml with Xml namespaces
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 ?
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 =
@"true1";
//string theXml = @"true1";
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
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"
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;
}