cbutler cbutler - 6 months ago 45
C# Question

C# find xml element matching two element levels using linq

I am trying to select an XElement from an xml document and match on two "levels" of the xml document. my file structure is:

<Library Name="Main" Path="C:\somefile.Xml">
<ReadingList Name="Test1">
<ReadingList Name="Test2">
<Library Name="Backup" Path="C:\somefile.Xml">

And I want to find the reading list name "test2" in library "Main" so I can copy this element and all child elements to another Library node.

I would prefer a solution using linq as I am trying to learn this.

Thanks in advance for any help

When I add a new "reading list" I do it this way:

public void AddReadingList(string fullyQualifiedPath, Library lib, string name)
XDocument xdoc = XDocument.Load(fullyQualifiedPath);

XElement library = xdoc.Element("eStack").Elements("Library")
.Single(x => x.Attribute("Name").Value == lib.Name);

library.Add(new XElement("ReadingList", new XAttribute("Name", name)));


but the operation I want to perform is a copy of this element and sub elements. The problem is, there may be more than one "library" element with the same name so I need to check the library name and reading list name. does that make sense?


Using a combination of .Descendants and Wheres will do the trick:

var result = XDocument.Load(fullyQualifiedPath)
         //Can replace with `FirstOrDefault` if you know there is only one
     .Where(element => element.Attribute("Name")?.Value == "Main") 
     .Where(element => element.Attribute("Name")?.Value == "Test2").ToList();

You can also use .Elements instead of Descendants I just preferred to use it so to not specify also the level of app or any other along the way.

For prior c# 6.0 you can do:

var result = XDocument.Load("data.xml")
     .Where(element =>
        var att = element.Attribute("Name");
        return att != null ? (att.Value == "Main" ? true : false) : false;
         //Make sure to do above change here too
     .Where(element => element.Attribute("Name")?.Value == "Test2").ToList();

Or if creating a method to help with it instead of repeating the code:

 Func<XElement, string, string> tryGetAttributeValue = (element, attributeName) =>
    var attribute = element.Attribute(attributeName);
    return attribute == null ? string.Empty : attribute.Value;

 var result = XDocument.Load("data.xml")
     .Where(element => tryGetAttributeValue(element,"Name") == "Main")
     .Where(element => tryGetAttributeValue(element, "Name") == "Test2").ToList();