Search Xdocument Using Linq Without Knowing the Namespace

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



Leave a reply



Submit