Robert Smith Robert Smith - 2 months ago 12
C# Question

Retrieving specific elements from an XDocument based on a list

I have a list of xml elements stored in a class:

public class clsField
{
public string fieldName { get; set; }
}


Then I am loading an xml file:

XDocument doc = XDocument.Load(fileName);


Finally I want to retrieve only the fields defined in the above class into a IEnumerable object. This is what I have so far:

List<clsField> lstFieldsToProcess;
IEnumerable<XElement> allthedocs = from thedoc
in doc.Descendants("thedocs")
select
(
from fields
in lstFieldsToProcess
select XElement.Parse(fields.fieldName)
);


But am getting an error on the above code. Any help would be appreciated. Thanks before hand.

Update #1

Pseudo code:

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

Answer

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.