Search XDocument using LINQ without knowing the namespace
As Adam precises in the comment, XName are convertible to a string, but that string requires the namespace when there is one. That's why the comparison of .Name to a string fails, or why you can't pass "Person" as a parameter to the XLinq Method to filter on their name.
XName consists of a prefix (the Namespace) and a LocalName. The local name is what you want to query on if you are ignoring namespaces.
Thank you Adam :)
You can't put the Name of the node as a parameter of the .Descendants() method, but you can query that way :
var doc= XElement.Parse(
@"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<Request xmlns=""http://CompanyName.AppName.Service.ContractA"">
<Person>
<CreditCardNumber>83838</CreditCardNumber>
<FirstName>Tom</FirstName>
<LastName>Jackson</LastName>
</Person>
<Person>
<CreditCardNumber>789875</CreditCardNumber>
<FirstName>Chris</FirstName>
<LastName>Smith</LastName>
</Person>
</Request>
</s:Body>
</s:Envelope>");
EDIT : bad copy/past from my test :)
var persons = from p in doc.Descendants()
where p.Name.LocalName == "Person"
select p;
foreach (var p in persons)
{
Console.WriteLine(p);
}
That works for me...
Parse XDocument without having to keep specifying the default namespace
The theory is that the meaning of the document is not affected by the user's choice of namespace prefixes. So long as the data is in the namespace http://www.secretsonline.gov.uk/secrets, it doesn't matter whether the author chooses to use the prefix "s", "secrets", "_x.cafe.babe", or the "null" prefix (that is, making it the default namespace). Your application shouldn't care: it's only the URI that matters. That's why your application has to specify the URI.
Ignore namespaces in LINQ to XML
Instead of writing:
nodes.Elements("Foo")
write:
nodes.Elements().Where(e => e.Name.LocalName == "Foo")
and when you get tired of it, make your own extension method:
public static IEnumerable<XElement> ElementsAnyNS<T>(this IEnumerable<T> source, string localName)
where T : XContainer
{
return source.Elements().Where(e => e.Name.LocalName == localName);
}
Ditto for attributes, if you have to deal with namespaced attributes often (which is relatively rare).
[EDIT] Adding solution for XPath
For XPath, instead of writing:
/foo/bar | /foo/ns:bar | /ns:foo/bar | /ns:foo/ns:bar
you can use local-name()
function:
/*[local-name() = 'foo']/*[local-name() = 'bar']
Linq-to-XML get elements with namespace
Here is one way of ignoring default namespaces, if you really want to do that:
XDocument doc;
using (XmlTextReader xr = new XmlTextReader("input.xml"))
{
xr.Namespaces = false;
doc = XDocument.Load(xr);
}
foreach (XElement bar in doc.Descendants("bar"))
But I would suggest to accept the existence and importance of namespaces in XML and use the XName and XNamespace objects LINQ to XML provides to work with them.
Linq-to-XML with XDocument namespace issue
The URL itself is irrelevant here - it's just a token for the namespace, really. I don't believe LINQ to XML will try to fetch it.
However, you need to use it to construct the XName
to search for:
XNamespace ns = "https://www.sisow.nl/Sisow/REST";
var banks = doc.Descendants(ns + "issuer")
.Select(x => new Bank((int) x.Element(ns + "issuerid"),
(string) x.Element(ns + "issuername"))
.ToList();
XDocument get element that defines it's own namespace
Element
only returns elements directly beneath the current node. You need the to specify the entire path (note there are multiple namespaces):
XNamespace ns = "urn:schemas-upnp-org:device-1-0";
XNamespace av = "urn:schemas-sony-com:av";
var api_list = xDoc.Root.Element(ns + "device")
.Element(av + "X_ScalarWebAPI_DeviceInfo").Elements();
Query an XDocument for elements by name at any depth
Descendants should work absolutely fine. Here's an example:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = @"
<root>
<child id='1'/>
<child id='2'>
<grandchild id='3' />
<grandchild id='4' />
</child>
</root>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("grandchild"))
{
Console.WriteLine(element);
}
}
}
Results:
<grandchild id="3" />
<grandchild id="4" />
Query XDocument with xmlns attribute (namespace)
Why is it a problem when a xml document contains an xmlns attribute?
It's not, if you understand what it means :) Basically you've applied a default namespace URI of "http://schemas.microsoft.com/developer/msbuild/2003" to all elements. So when querying, you need to specify that namespace too. Fortunately, LINQ to XML makes that really simple:
XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
XDocument doc = XDocument.Parse(xml2);
foreach (XElement element in doc.Descendants(ns + "ItemGroup"))
{
Console.WriteLine(element);
}
Use LINQ XML with a namespace
Namespaces in XML can be tricky. I've run into this problem myself a number of times. In all likelihood, the following will fix your problem:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(doc.Root.Name.Namespace.GetName("Id"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
Basically, this just assumes the underlying element to have the same namespace as your root element. That's true in this case, but of course it doesn't have to be.
The right way, probably, is to do it explicitly. Now, granted, that kind of depends on how you're using this and your datasource, so make the decision for yourself, but that would require doing something more like this:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(System.Xml.Linq.XName.Get("Id", "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
The cause for your problem was that the default behavior for XElement
, when not given an explicit namespace, is to assume no namespace. However, the default behavior for the XML spec is to assume the parent's namespace. In your case, those two were different, so it wasn't able to find the descendant.
Related Topics
Creating Simple C++.Net Wrapper. Step-By-Step
How to Make the .Net Httpclient Use Http 2.0
How to Detect Iis Version Using C#
Perform Button Click Event When User Press Enter Key in Textbox
Regular Expression to Extract HTML Body Content
Setup Database (Sqlite) for Unity
Mono: Is Remote Debugging Possible with Monodevelop
Why Is ASP.NET Identity Identitydbcontext a Black-Box
Getting a Request.Headers Value
Non Client Painting on Aero Glass Window
Check If a String Is a Palindrome
Dependency Injection with a Static Logger, Static Helper Class
Detect Browser Close on Asp.Net
Convert from Word Document to HTML