Query an Xdocument for Elements by Name at Any Depth

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

How to get the right elements with XDocument query

You can get all the Component by using the Elements method and then select Name inside those components like this:-

var names = myXMLDoc.Descendants("Step")
.Where(x => (string)x.Attribute("id") == "id_3")
.Elements("Component")
.Select(x => (string)x.Element("Name"));

Here, names will return IEnumerable<string> on which you can easily iterate through foreach loop.

Working Fiddle.

Finding element in XDocument?

Elements() will only check direct children - which in the first case is the root element, in the second case children of the root element, hence you get a match in the second case. If you just want any matching descendant use Descendants() instead:

var query = from c in xmlFile.Descendants("Band") select c;

Also I would suggest you re-structure your Xml: The band name should be an attribute or element value, not the element name itself - this makes querying (and schema validation for that matter) much harder, i.e. something like this:

<Band>
<BandProperties Name ="Doors" ID="222" started="1968" />
<Description>regular Band<![CDATA[lalala]]></Description>
<Last>1</Last>
<Salary>2</Salary>
</Band>

Find Elements by Attribute using XDocument

change Users in the 2nd line to User. Like this:

    IEnumerable<XElement> users = (from el in XMLDoc.Root.Elements("User")
where (string)el.Attribute("GUID") == userGUID.ToString()
select el);

I'm assuming XMLDoc is an XDocument, and not the root element itself.

Retrieving specific elements from an XDocument based on a list

I am not quite sure what you try to do. Do you want to filter out all elements with a certain name and that is descendant of the element(s) named "thedocs"?

Assuming that is what you want to do, then you could do the following:

IEnumerable<XElement> elems = from field in lstFieldsToProcess
from de in doc.Elements("thedocs").Descendants(field.fieldName)
select de;

// Of course this is the same:
IEnumerable<XElement> elems = lstFieldsToProcess
.SelectMany(f => doc.Elements("thedocs").Descendants(f.fieldName));

I am not sure how efficient XDocument is though, so perhaps it is better to take out the fieldnames you want to match first (please test which is the better of the two if performance is important to you):

var fieldNames = new HashSet<string>(lstFieldsToProcess.Select(c => c.fieldName));
IEnumerable<XElement> elems = from d in doc.Elements("thedocs").Descendants()
where fieldNames.Contains(d.Name.LocalName)
select d;

Update #1
I think almost have it yourself in your pseudocode.

var fieldNames = new HashSet<string>(lstFieldsToProcess.Select(c => c.fieldName));
IEnumerable<XElement> elems = from level1 in doc.Elements("thedocs")
from level2 in level1.Descendants()
where fieldNames.Contains(level2.Name.LocalName)
select level1;

Though, this can contain same "thedocs" element many times (if it has several descendants with valid fieldname). To avoid that, just call Distint() on the result.

Find elements by attribute name and its value using XDocument

If you are sure you exception is thrown because you table element may come without class attribute, then you could do this instead:

XElement table =
d.Descendants("table").
SingleOrDefault(e => ((string)e.Attribute("class")) == "imgcr");

In that case you are casting a null value to string, which is null at the end, so you are comparing null == "imgcr", what is false.

You can check this msdn page if you need more info about how to retrieve the value of an attribute. There you will find this affirmation:

You can cast an XAttribute to the desired type; the explicit
conversion operator then converts the contents of the element or
attribute to the specified type.

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);
}

Query for unique id with XDocument

You can use LINQ to XML to retrieve the unique ID from your XML document.

string xml = "<Request><Person xmlns='http://CompanyName.AppName.version1' uniqueID='3221'><AccountNo>83838</AccountNo><FirstName>Tom</FirstName><LastName>Jackson</LastName></Person><Person xmlns='http://CompanyName.AppName.version1' uniqueID='21132'><AccountNo>789875</AccountNo><FirstName>Chris</FirstName><LastName>Smith</LastName></Person></Request>";

XDocument doc = XDocument.Parse(xml);
XNamespace ns = "http://CompanyName.AppName.version1";
var uniqueIDs = doc.Descendants(ns + "Person")
.Select(p => p.Attribute("uniqueID").Value)
.ToList();

Adding and removing XDocument elements and their childs with LINQ

Adding elements to XML is pretty straightforward. Using the right indentation you can even see the XML structure in your code:

XDocument doc = XDocument.Load(fileName);

doc.Root.Add(new XElement("employee", new XAttribute("id", 42),
new XElement("personalInfo",
new XElement("name", "Arthur Dent"),
new XElement("zip", "00000")),
new XElement("employeeInfo",
new XElement("salary", 0),
new XElement("id", 3))));

doc.Save(fileName);


Related Topics



Leave a reply



Submit