限制查询以忽略元素后代

时间:2013-02-22 12:14:47

标签: c# linq linq-to-xml

我正在尝试查询以下XML以构建一些复制XML中概述的文件夹层次结构的对象。

<ShareList>
    <Title>Documantis</Title>
    <Url>/sites/dev/Documantis/Forms/AllItems.aspx</Url>
    <Guid>fed8f456-efa9-4fe5-8b97-46734a3040b6</Guid>
    <HasUniqueScopes>False</HasUniqueScopes>
    <RootFolder>/sites/dev</RootFolder>
    <Children>
        <ShareListItem>
            <Title>First</Title>
            <Url>Documantis/First</Url>
            <HasUniqueRole>False</HasUniqueRole>
            <IsSubFolder>False</IsSubFolder>
            <PermissionMask>FullMask</PermissionMask>
            <Children>
                <ShareListItem>
                    <Title>Second</Title>
                    <Url>Documantis/First/Second</Url>
                    <HasUniqueRole>False</HasUniqueRole>
                    <IsSubFolder>False</IsSubFolder>
                    <ParentGuid>22b2a7e9-a42e-497f-aad3-8caa85f6ac6d</ParentGuid>
                </ShareListItem>
            </Children>
        </ShareListItem>
        <ShareListItem>
            <Title>Folda</Title>
            <Url>Documantis/Folda</Url>
            <HasUniqueRole>False</HasUniqueRole>
            <IsSubFolder>False</IsSubFolder>
            <PermissionMask>FullMask</PermissionMask>
        </ShareListItem>
    </Children>
</ShareList>

我很难找到一种方法一次返回<ShareListItem>个元素的一个级别,使用我当前的代码,它返回一个List中的所有ShareListItems,它们不能准确地表示层次结构。

XmlDocument doc = new XmlDocument();
doc.LoadXml(sharepointXml);

XElement root;
using (XmlReader xr = new XmlNodeReader(doc)) { root = XElement.Load(xr); }

var result = from child in root.DescendantsAndSelf("ShareList") //.Elements("ShareList") // Descendants("ShareList")
             select child;

foreach (XElement xml in result)
{
    // Build ListItem from results
    ShareList list = new ShareList()
    {
        Title = xml.Element("Title").Value,
        Url = xml.Element("Url").Value,
        Guid = xml.Element("Guid").Value,
        HasUniqueScopes = Convert.ToBoolean(xml.Element("HasUniqueScopes").Value),
        RootFolder = xml.Element("RootFolder").Value,
    };

    if (xml.Element("Children") != null)
    {
        var subResult = from child in xml.Element("Children").Descendants("ShareListItem")
                        select child;

        foreach (XElement subXml in subResult)
        {
            // results here are flat and don't show depth of nodes
        }
        //list.Children =
    }

我可以从URL元素递归推断层次结构的结构,但是我已经用XML表示了它,所以我宁愿学习如何通过查询返回它。

修改

这是我最终使用的

public List<ShareList> HandleLists(XElement levelRoot)
{
    List<ShareList> lists = new List<ShareList>();

    var results = from list in levelRoot.DescendantsAndSelf("ShareList")
                 select list;

    foreach (var list in results)
    {
        var children = list.Element("Children");
        if (children == null)
            return null;

        ShareList shareList = new ShareList()
        {
            Title = list.Element("Title").Value,
            Url = list.Element("Url").Value,
            Guid = list.Element("Guid").Value,
            HasUniqueScopes = Convert.ToBoolean(list.Element("HasUniqueScopes").Value),
            RootFolder = list.Element("RootFolder").Value,
            // Recursively find ListItem folders
            Children = HandleSubfolders(list)
        };
        lists.Add(shareList);
    }
    return lists;
}

public List<ShareListItem> HandleSubfolders(XElement levelRoot)
{
    List<ShareListItem> subfolders = new List<ShareListItem>();

    // All nodes deeper than current
    var children = levelRoot.Element("Children");
    if (children == null)
        return null;

    // Subfolders
    var items = children.Elements("ShareListItem");
    foreach (var item in items)
    {
        ShareListItem listItem = new ShareListItem()
        {
            Title = item.Element("Title").Value,
            Url = item.Element("Url").Value,
            HasUniqueRole = Convert.ToBoolean(item.Element("HasUniqueRole").Value),
            IsSubfolder = Convert.ToBoolean(item.Element("IsSubFolder").Value),
            PermissionMask = item.Element("PermissionMask").Value,
            PermissionMaskName = item.Element("PermissionMaskName").Value,
            // Recursively find ListItem subfolders
            Children = HandleSubfolders(item)
        };
        // Add subfolder to Children collection
        subfolders.Add(listItem);
    }
    return subfolders;
}

3 个答案:

答案 0 :(得分:2)

你想在这里使用递归。

创建一个方法来处理层次结构的一个级别,并使用下一个级别调用自身。

public void HandleLevel(XElement levelRoot)
{
    PerformAction(levelRoot);

    var children = levelRoot.Element("Children");
    if(children == null)
        return;
    var items = children.Elements("ShareListItem");
    foreach(var item in item)
    {
        // Handle child's children:
        HandleLevel(item);
    }
}

PerformAction是实际执行的代码,无论您想为每个文档执行什么操作 代码当前的结构方式,也会对根文档/sites/dev/Documantis/Forms/AllItems.aspx执行此操作 如果您不想这样,只需将PerformAction移至foreach循环并传递item而不是levelRoot

BTW:您对根元素的初始化非常很奇怪 你可以简单地使用它:

var root = XDocument.Parse(sharepointXml).Root;

HandleLevel的初始调用看起来像这样:

HandleLevel(root);

答案 1 :(得分:1)

这样做的一种方法是创建类来表示层次结构,如下所示:

public class ShareList {
    ...
    public List<ShareList> Children { get; set; }
}

在您的代码中,将遍历部分重构为一个接受Sharelist节点并遍历它的方法,为每个Children节点调用自身:

private Sharelist RecurseHierarchy(XElement sharelistNode, ShareList parent)
{
    // your traversing code goes here
    // get your data and create a new Sharelist object
    // if it has a children node, traverse it and call this same method on the child
    // Sharelist nodes
    parent.Title = sharelistNode.Element("Title").Value;        


    var children = sharelistNode.Element("Children");

    if (children != null)
    {
        var items = children.Elements("ShareListItem");
        foreach(var listItem in items)
        {
            ShareList childShareList = new ShareList();
            parent.Children.Add(childShareList);

            RecurseHierarchy(listItem, childShareList);
        }
    }

    // Just in case we want to chain some method
    return parent;
}

要最初调用它,您必须传入根节点和新的ShareList对象。

答案 2 :(得分:1)

产生结果的好方法是使用XPath(here是一个很好的入门,如果你需要的话)。

将XML加入XmlDocument后,可以使用XPathNavigator返回不同的位,如下所示:

var xmlNavigator = xmlDocument.CreateNavigator();

var outerQuery = xmlNavigator.Select("ShareList/Children/ShareListItem");

while (outerQuery.MoveNext()) {
    Console.WriteLine(outerQuery.Current.SelectSingleNode("Title").Value);
    var innerQuery = outerQuery.Current.Select("Children/ShareListItem");
    while (innerQuery.MoveNext()) {
        Console.WriteLine(" - " + innerQuery.Current.SelectSingleNode("Title").Value);
    }
}

在上面的代码中,我们查询根目录ShareList节点的Children节点中的所有ShareListItem节点的XML,并将结果XPathNodeIterator存储在变量outerQuery中。然后,我们遍历找到的所有节点,并在每个节点上运行操作以及另一个XPath查询以检索要处理的子节点。上面的代码产生以下输出:

  

首先
    - 第二个   Folda

我认为你追求的是什么。显然,如果你的XML可以比这更深入地嵌套,你可以使用递归。

相关问题