LINQ to XML递归查询 [英] LINQ to XML recursive query

查看:79
本文介绍了LINQ to XML递归查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个像文档树一样的xml站点地图,看起来像这样:

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

*请注意,这实际上不是我的xml文件.实际的xml文件太大,无法显示.基本上,您需要避免的是彼此之间可能嵌套有"X"个文件夹,并且在这些文件夹中的某个点上,可能有"X"个文件以及子文件夹. /p>

还为一些文件夹提供了安全性,该安全性由该文件夹中的一切继承(文件,子文件夹,子文件夹中的文件等).我试图提出一个LINQ to XML查询,以基于该文件GUID获取给定文件的安全性,并且它对于1级和2级文件正常工作,但是当我尝试在3个文件夹深的文件,它失败,并且我得到一个nullreference异常.这是我正在使用的查询:

XDocument sitemap = XDocument.Load(HttpContext.Current.Server.MapPath("/.../sitemap.xml"));
        XElement fileFromMap =
            sitemap.Descendants("File").Where(
            file => file.Attribute("GUID").Value == guid).First();

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

*和应归还的信用额,我在此处 a>

currentFile变量的声明中发生了nullreference异常,但我不确定为什么.我确保Guid匹配...并且由于正确声明了fileFromMap,因此我知道已找到我的文件.我认为这里需要做的是更好地递归检查父文件夹的安全性.一旦找到任何安全性,查询就可以停止,因为该网站的设置方式应该没有冲突的安全性声明. (例如,没有定义安全性的文件夹将不在已经定义安全性的文件夹之内)

如果我错了,而这不是我需要做的,请提供您可能有的任何建议,并随时更改此问题的标题以更好地记录它.

解决方案

(如果您在上一个问题中给我留下评论,那可能是最好的选择,但是现在我最好回答这个问题;)

我提供的代码中有一个错误:我以为SelectMany()折叠null项的方式与XElement.Add()相同,但我错了.如果祖先链中没有文件夹,则SelectMany()会抛出NullReferenceException.

我们只需要提供一个空的XElement枚举即可解决该问题:

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

我将使用反向链接更新原始答案.

I have an xml sitemap structured like a document tree, such that it looks like this:

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

*Note that this is NOT my actually xml file. The actual xml file is too big to just show. Basically what you need to take away from this is that there are potentially 'X' amount of folders nested within each other, and at some point in those folders, there can be 'X' amount of files, as well as children folders.

Also, some folders are given security, which is inherited by everything in that folder (files, child folders, files within child folders, etc). I am trying to come up with a LINQ to XML query to get the security of a given file based on that files GUID, and it works fine for level-1 and level-2 files, but when I try to run the query on a file that is 3 folders deep, it fails and I get a nullreference exception. Here is the query I am using:

XDocument sitemap = XDocument.Load(HttpContext.Current.Server.MapPath("/.../sitemap.xml"));
        XElement fileFromMap =
            sitemap.Descendants("File").Where(
            file => file.Attribute("GUID").Value == guid).First();

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

*and credit where credit is due, I got this query here

The nullreference exception is happening in the declaration of the currentFile variable, and I'm not sure why. I've made sure that the Guids match up...and since fileFromMap is being declared correctly, I know that my file is being found. I assume what needs to be done here is something to better check parent folders recursively for security. The query can stop as soon as it finds any security, because the way the site is set up, there should be no conflicting security declarations. (eg, no folder that has defined security will be within a folder that already has defined security)

If I am wrong, and this isn't what I need to do, please offer any suggestions you may have, and feel free to change the title of this question accordingly as to better document it.

解决方案

(It would probably have been best if you left me a comment in your previous question instead, but I might as well answer this one now that it's posted ;)

There's a mistake in the code I provided: I thought SelectMany() collapsed null items in the same way XElement.Add() does, and I was wrong. If there is a folder without roles in the ancestor chain, SelectMany() will throw a NullReferenceException.

We only need to feed it an empty enumerable of XElement to solve the problem:

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

I'll update my original answer with a backlink.

这篇关于LINQ to XML递归查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆