Query an XDocument for elements by name at any depth

2,184

Solution 1

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" />

Solution 2

An example indicating the namespace:

String TheDocumentContent =
@"
<TheNamespace:root xmlns:TheNamespace = 'http://www.w3.org/2001/XMLSchema' >
   <TheNamespace:GrandParent>
      <TheNamespace:Parent>
         <TheNamespace:Child theName = 'Fred'  />
         <TheNamespace:Child theName = 'Gabi'  />
         <TheNamespace:Child theName = 'George'/>
         <TheNamespace:Child theName = 'Grace' />
         <TheNamespace:Child theName = 'Sam'   />
      </TheNamespace:Parent>
   </TheNamespace:GrandParent>
</TheNamespace:root>
";

XDocument TheDocument = XDocument.Parse( TheDocumentContent );

//Example 1:
var TheElements1 =
from
    AnyElement
in
    TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
select
    AnyElement;

ResultsTxt.AppendText( TheElements1.Count().ToString() );

//Example 2:
var TheElements2 =
from
    AnyElement
in
    TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
where
    AnyElement.Attribute( "theName" ).Value.StartsWith( "G" )
select
    AnyElement;

foreach ( XElement CurrentElement in TheElements2 )
{
    ResultsTxt.AppendText( "\r\n" + CurrentElement.Attribute( "theName" ).Value );
}

Solution 3

You can do it this way:

xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")

where xml is a XDocument.

Be aware that the property Name returns an object that has a LocalName and a Namespace. That's why you have to use Name.LocalName if you want to compare by name.

Solution 4

Descendants will do exactly what you need, but be sure that you have included a namespace name together with element's name. If you omit it, you will probably get an empty list.

Solution 5

There are two ways to accomplish this,

  1. LINQ to XML
  2. XPath

The following are samples of using these approaches,

List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();

If you use XPath, you need to do some manipulation with the IEnumerable:

IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();

Note that

var res = doc.XPathEvaluate("/emails/emailAddress");

results either a null pointer, or no results.

Share:
2,184

Related videos on Youtube

g.mark11
Author by

g.mark11

Updated on December 17, 2020

Comments

  • g.mark11
    g.mark11 over 3 years

    I need a success message to pop-up when a user hits the Submit button. I tried to make a javascript file to solve the task, but something should be wrong because it does nothing.

    The Button(HTML):

    <button name="submit" type="submit" class="btn btn-primary btn-xl" id="sendMessageButton" action="form.js">Küldés</button>
    

    Javascript file:

    $("#sendMessageButton").click(function(){
    Swal.fire(
        'Good job!',
        'You clicked the button!',
        'success'
        );
    });
    

    Console Error:

    Uncaught ReferenceError: Swal is not defined at HTMLButtonElement.<anonymous> (form.js:2) at HTMLButtonElement.dispatch (jquery.min.js:2) at HTMLButtonElement.y.handle (jquery.min.js:2)
    
    • Rambarun Komaljeet
      Rambarun Komaljeet almost 5 years
      do you get any errors in the browser js console ?
    • g.mark11
      g.mark11 almost 5 years
      Uncaught ReferenceError: Swal is not defined at HTMLButtonElement.<anonymous> (form.js:2) at HTMLButtonElement.dispatch (jquery.min.js:2) at HTMLButtonElement.y.handle (jquery.min.js:2)
    • DaWe
      DaWe over 3 years
      "Küldés" - magyarok mindenhol 😃
  • EoRaptor013
    EoRaptor013 over 13 years
    But, what if my source xml doesn't have a namespace? I suppose I can add one in code (have to look into that), but why is that necessary? In any event, root.Descendants("myTagName") doesn't find elements buried three or four levels deep in my code.
  • SoftwareSavant
    SoftwareSavant about 12 years
    Don't want to be mean here, but your example does not show grandchildren. emailAddress is a child of emails. I am wondering if there is a way to use Descendants without using namespaces?
  • pfeds
    pfeds over 11 years
    How would you tackle this if an element name was duplicated within an xml document? For example: If the xml contained a collection of <Cars> with sub elements of <Part>, and also a collection of <Planes> with sub elements of <Part>, and you want a list of Parts for Cars only.
  • Jon Skeet
    Jon Skeet over 11 years
    @pfeds: Then I'd use doc.Descendants("Cars").Descendants("Part") (or possibly .Elements("Part") if they were only direct children.
  • Tahir Hassan
    Tahir Hassan about 11 years
    just to mention that XPathEvaluate is in the System.Xml.XPath namespace.
  • The Dag
    The Dag almost 11 years
    XPathEvaluate should do the trick, but your query only takes nodes at a particular depth (one). If you wanted to select all elements named "email" regardless of where in a document they occur, you'd use the path "//email". Obviously such paths are more expensive, since the entire tree must be walked whatever the name is, but it can be quite convenient - provided you know what you're doing.
  • Kim
    Kim over 10 years
    Thanks! We're using datacontract serialization. This creates a header like <MyClassEntries xmlns:i="w3.org/2001/XMLSchema-instance" xmlns="schemas.datacontract.org/2004/07/DataLayer.MyClass"> and I was stumped why I wasn't getting any descendants. I needed to add the {schemas.datacontract.org/2004/07/DataLayer.MyClass} prefix.
  • EvilDr
    EvilDr over 8 years
    Six years on and still a fantastic example. In fact, this is still far more helpful than the MSDN explanation :-)
  • Dror Harari
    Dror Harari over 8 years
    And it is still an evil example, Dr., since if there are no "Cars", the above code would result in an NPE. Maybe the .? from the new C# will finally make it valid
  • DareDude
    DareDude over 7 years
    @DrorHarari Nope, no exception is thrown: Try out var foo = new XDocument().Descendants("Bar").Descendants("Baz"); Because Descendants returns an empty IEnumerable<XElement>and not null.
  • Eugene  Maksimov
    Eugene Maksimov over 6 years
    I'm trying to get all EmbeddedResource node from c# project file, and this is only way that works. XDocument document = XDocument.Load(csprojPath); IEnumerable<XElement> embeddedResourceElements = document.Descendants( "EmbeddedResource"); Is not works and I don't understand why.
  • g.mark11
    g.mark11 almost 5 years
    No I haven't, this was the problem. Thank you :)
  • Maher Nabil
    Maher Nabil over 2 years
    after hours of searching and experimenting, this is the only answer that helped. Man can't thank you enough. Kudos for adding the namespace into the Descendants.