1

我正在尝试查询以下 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;
}
4

3 回答 3

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.

顺便说一句:您对根元素的初始化奇怪。
你可以简单地使用这个:

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

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

HandleLevel(root);
于 2013-02-22T12:20:19.287 回答
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 对象。

于 2013-02-22T12:34:55.593 回答
1

产生结果的一个好方法是使用 XPath(如果需要,这里是一个很好的入门指南)。

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

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

第一
-第二

我认为这是你所追求的。显然,如果您的 XML 可以嵌套得比这更深,您可以在必要时使用递归。

于 2013-02-22T12:46:56.697 回答