41
<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">
    <SetNationalList xmlns="http://www.lge.com/ddc">
      <nationalList>
        <portnumber>6000</portnumber>
        <slaveaddress>7000</slaveaddress>
        <flagzone>2</flagzone>
        <flagindivisual>5</flagindivisual>
        <flagdimming>3</flagdimming>
        <flagpattern>6</flagpattern>
        <flaggroup>9</flaggroup>
      </nationalList>
    </SetNationalList>
  </s:Body>
</s:Envelope>

XDocument xdoc = XDocument.Parse(xml);
foreach (XElement element in xdoc.Descendants("nationalList"))
{
   MessageBox.Show(element.ToString());
}

I'd like to iterate through every nodes under nationalList but it isn't working for me, it skips the foreach loop entirely. What am I doing wrong here?

arserbin3
  • 6,010
  • 8
  • 36
  • 52
TtT23
  • 6,876
  • 34
  • 103
  • 174

4 Answers4

99

You're not including the namespace, which is "http://www.lge.com/ddc", defaulted from the parent element:

XNamespace ns = "http://www.lge.com/ddc";
foreach (XElement element in xdoc.Descendants(ns + "nationalList"))
{
    ...
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Crap I never noticed the namespace. I will try this and see if it works. – TtT23 Aug 13 '12 at 11:54
  • 1
    @l46kok: At least specifying namespaces in LINQ to XML is almost trivial :) – Jon Skeet Aug 13 '12 at 11:56
  • 9
    TIL: That xml namespaces hurt and that `Descendants`/`Element`/`Elements` need to prepend NS. *sigh*. I welcome my json overlords.... – Pure.Krome Dec 14 '14 at 23:00
  • And is there anyway to do this one time only, in the code? – Chucky Apr 21 '16 at 09:37
  • 1
    @Chucky: Do *what* one-time only? It's not clear what you're asking. If you mean "do you have to specify the namespace every time you want to use a fully-qualified name" then yes... although if you're using the same name multiple times, you could declare that once, e.g. `XName nationalListName = ns + "nationalList";` – Jon Skeet Apr 21 '16 at 10:06
  • Yeah I meant the first! Okay, I'll just remember to specify every time. Thanks Jon. – Chucky Apr 21 '16 at 10:08
  • 2
    This throws an exception saying that ":" is not valid in a name. Worked when i surrounded the namespace with "{http://....}" – Dan Mar 17 '17 at 09:37
  • @Dan: No it doesn't. I've just tried it, and run it with the XML in the question. It's fine. I suspect you've done something slightly different, and that's where the error is. But you haven't told us what you've done, so we can't really help... Perhaps you should ask a new question with a [mcve] - linking to this one, but showing the error where you've tried to use this answer. – Jon Skeet Mar 17 '17 at 09:39
  • 2
    Yeah, i see where mine is different; i inserted the namespace as a string instead of a 'XNamespace' .. apologies – Dan Mar 17 '17 at 09:43
10

You have to use the namespace:

// do _not_ use   var ns = ... here.
XNameSpace ns = "http://www.lge.com/ddc";

foreach (XElement element in xdoc.Descendants(ns + "nationalList")
{
      MessageBox.Show(element.ToString());
}
H H
  • 263,252
  • 30
  • 330
  • 514
3

If you don't want to have to use the ns prefix in all the selectors you can also remove the namespace upfront when parsing the xml. eg:

string ns = "http://www.lge.com/ddc";
XDocument xdoc = XDocument.Parse(xml.Replace(ns, string.Empty));

foreach (XElement element in xdoc.Descendants("nationalList")
...
Ben Osborne
  • 1,412
  • 13
  • 20
derekadk
  • 214
  • 2
  • 6
  • 2
    If you don't want to have to hard-code the namespace as a string literal, or you don't know it at compile time, you can ask the doc for it like this: `string ns = xdoc.Root.Name.Namespace;` – Jinlye Jul 15 '20 at 10:00
-3

It is correct that you have to include the namespace, but the samples above do not work unless you put the namespace in curly braces:

XNameSpace ns = "http://www.lge.com/ddc";

foreach (XElement element in xdoc.Descendants("{" + ns + "}nationalList")
{
      MessageBox.Show(element.ToString());
}

Greetings Christian

Christian Moser
  • 1,871
  • 20
  • 10
  • 2
    The samples above do indeed work. `ns + test` returns a `System.Xml.Linq.XName` object that will correctly have the ToString of `{myNamespace}test`. I think you probably got confused because this **will** return wrong results: `String.Format("{0}test", ns)` (it gets the ToString of `ns` instead of making an XName object) – arserbin3 May 26 '14 at 20:29
  • Christian is correct if you are using a string and not a Xnamespace etc. you will need {} or you will get an error as for a start names cannot have ":" in them, either way works. – Skyline Nov 17 '17 at 13:06