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>

一度に 1 レベルの要素を返す方法を見つけるのに苦労<ShareListItem>しています。現在のコードでは、階層を正確に表していない 1 つのリストですべての 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

ここで再帰を使用することをお勧めします。

階層の1つのレベルを処理し、次のレベルでそれ自体を呼び出すメソッドを作成します。

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して、の代わりにパスします。foreachitemlevelRoot

ところで:ルート要素の初期化は非常に奇妙です。
あなたは単にこれを使うことができます:

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

の最初の呼び出しは、次のHandleLevelようになります。

HandleLevel(root);
于 2013-02-22T12:20:19.287 に答える
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);
    }
}

上記のコードでは、ルート ShareList ノードの Children ノード内のすべての ShareListItem ノードの XML を照会し、結果の XPathNodeIterator を変数 outerQuery に格納します。次に、見つかったすべてのノードを反復処理し、それぞれに対して操作と別の XPath クエリを実行して、処理する子ノードを取得します。上記のコードは、次の出力を生成します。

1
番目 - 2 番目
のフォルダ

私はそれがあなたが求めているものだと思います。明らかに、XML をこれよりも深くネストできる場合は、必要に応じて再帰を使用できます。

于 2013-02-22T12:46:56.697 に答える
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 オブジェクトを渡す必要があります。

于 2013-02-22T12:34:55.593 に答える