LINQ to XML querying for sibling elements

2019-05-11 00:40发布

问题:

Sorry if this question is very basic, but I haven't worked with XML very much, and this is my first time working with LINQ to XML...

I have an XML sitemap that is structured like a directory tree:

<Site>
    <File GUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">FileName</file>
    <Folder name="FolderName">
        <Security>
            <Role>Admin</role>
        </Security>
        <File GUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">FileName</file>
        <Folder name="subFoler">
            <File GUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">FileName</file>
            <File GUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">FileName</file>
            <Folder>
                <File GUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">FileName</file>
            </Folder>
        </Folder>
    </Folder>
</Site>

In this way, every file will inherit security from its parent folder (or grandparent folder, etc, depending upon how far up the tree the <security></security> is located.

I want to be able to, given a file's GUID, select that file with LINQ, and gather all of the roles associated with the security that file has inherited.

This is what I have so far, but it is a very rough attempt, and is not complete:

XDocument sitemap = XDocument.Load(@"\sitemap.xml");
XElement currentFile = new XElement("File", 
        from file in sitemap.Elements("File")
        where file.Element("GUID").Equals(guid)
        select file.Parent.Element("security").Elements("role"));

ps. The sitemap file is located within the same directory as the class in which this code is written

回答1:

It looks like you want to walk up the folder ancestor chain and accumulate the roles you find along the way. You can do that with the aptly-named Ancestors() and SelectMany() methods:

XElement fileFromMap = sitemap.Descendants("File").Where(
    file => file.Attribute("GUID").Value == guid.ToString("D")).Single();

XElement currentFile = new XElement("File",
    fileFromMap.Value,
    fileFromMap.Ancestors("Folder").SelectMany(
        folder => {
            XElement security = folder.Element("Security");
            return (security != null
                ? security.Elements("Role") : new XElement[0]);
        }));

EDIT: Changed element capitalization to match the updated question.

EDIT 2: The code above was mistakenly feeding a null item to SelectMany() (which is far less indulgent than XElement.Add() in this regard), and this resulted in a NullPointerException. It has been updated to return an empty XElement array instead.



回答2:

Try this (I usually do Method syntax but you should be able to convert to query syntax)

        XElement currentFile = new XElement("File",
            sitemap.Descendants("file").Where(
               file => file.Attribute("GUID").Value.Equals(guid)).Select(
                   file => file.Parent.Element("security").Elements("role")));

Here currentFile should have

<File>
  <role>Admin</role>
</File>