2013-02-22 47 views
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>元素之一的水平,我当前的代码返回所有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; 
} 

回答

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); 
+0

感谢您的回答,我喜欢这个例子是如何分解成小的方法,我认为这使得代码更容易维护。 – Amicable 2013-02-22 13:48:18

1

的一种方法是通过创建类来表示,像这样的层次结构:

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

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

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对象传递。

+0

这就是我的ShareList类的工作原理:) 感谢您的回答! – Amicable 2013-02-22 14:08:52

1

使用XPath生成结果的好方法是使用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); 
    } 
} 

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

首先
- 二
Folda

这一点我觉得你以后。显然,如果您的XML可以嵌套得比这更深,您可以根据需要使用递归。