4

I am trying to find nodes in an XML document like this:

<?xml version="1.0"?>
<TrainingCenterDatabase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
  <Activities>
    <Activity Sport="CyclingTransport">
      <Id>2014-07-08T15:28:14Z</Id>
    </Activity>
  </Activities>
</TrainingCenterDatabase>

I aim to extract the node value 'Id' with code like this:

XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants("Id")
                          .Select(x => (string)x)
                          .ToList();
Console.WriteLine(urlList.Count);

However the count is 0, where I expect 1.

After some debugging and editing the XML I noticed that if I change the TrainingCenterDatabase node and remove the attributes to this:

<TrainingCenterDatabase>

Then the result is a count of 1 as expected.

So my question is how do I take into account the namespaces so that I can get the value when the TrainingCenterDatabase node has these attributes?

Dan Smith
  • 280
  • 3
  • 13
  • Another approach is to use LocalName instead which ignores namespaces: http://stackoverflow.com/questions/1145659/ignore-namespaces-in-linq-to-xml – AaronLS Aug 18 '14 at 00:46

2 Answers2

5

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.

Matthew Haugen
  • 12,916
  • 5
  • 38
  • 54
  • That's worked perfectly. So the reason that the list has a count of 0 in the original example is because it is assumed that the element doesn't have the same name space as the root element? – Dan Smith Aug 18 '14 at 00:39
  • 1
    @DanSmith good point, I didn't really explain what was wrong. I edited my answer. But yes, basically it was looking for an element with no namespace and a `Name` of "Id," which didn't match the element you were looking for which had a namespace. – Matthew Haugen Aug 18 '14 at 00:42
0

It Works...

        XDocument doc = XDocument.Load(filePath);
        XNamespace ns = "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2";
        var root = doc.Descendants(ns + "Id").Select(x => x.Value).ToList();
        Console.WriteLine(root.Count);
Thirisangu Ramanathan
  • 614
  • 1
  • 11
  • 20