C# LINQ到XML:上下移动节点最有效的方法是什么

C# LINQ到XML:上下移动节点最有效的方法是什么,c#,xml,linq,linq-to-xml,C#,Xml,Linq,Linq To Xml,我需要在某些节点之前和之后移动同级节点。这是我正在使用的代码 <tabs> <tab> <name>Overview</name> </tab> <tab> <name>Testing</name> </tab> <tab> <name>Performanc

我需要在某些节点之前和之后移动同级节点。这是我正在使用的代码

<tabs>
     <tab>
          <name>Overview</name>
     </tab>
     <tab>
          <name>Testing</name>
     </tab>
     <tab>
          <name>Performance</name>
     </tab>
     <tab>
          <name>Braking</name>
     </tab>
</tabs>

概述
测试
演出
刹车

我想在上面的概述中移动带有测试的选项卡。我如何使用linq to XML实现这一点?

您可以通过删除元素,然后将其重新插入到所需位置来移动元素:

var doc = XDocument.Parse(@"<tabs>...</tabs>");

var tab = doc.Root.Elements().ElementAt(1);
tab.Remove();
doc.Root.AddFirst(tab);

您可以使用以下内容:

    var tests = from node in doc.Descendants("name") where node.Value == "Testing" select node;
    var test = tests.Single();
    var tab = test.Parent;
    var tabs = tab.Parent;
    tab.Remove();
    tabs.AddFirst(tab);

不确定您的XML结构中有多少是固定/已知的。

对不起,这是VB.NET和XML文本,但可以用C#完成。这里的想法是使用
反向
扩展方法:

Sub Main()
        Dim tab = <tabs>
                      <tab>
                          <name>Overview</name>
                      </tab>
                      <tab>
                          <name>Testing</name>
                      </tab>
                      <tab>
                          <name>Performance</name>
                      </tab>
                      <tab>
                          <name>Braking</name>
                      </tab>
                  </tabs>
        Console.WriteLine(SwapElements("Testing", "Performance", tab).ToString)
        Console.ReadLine()
    End Sub
    Function SwapElements(ByVal firstElement As String, ByVal secondElement As String, ByVal tab As XElement) As XElement
        Dim swapped = tab.Elements.Where(Function(e) e.Value = firstElement Or e.Value = secondElement).Reverse
        Dim middle = tab.Elements.SelectMany(Function(e) e.ElementsAfterSelf.Where(Function(f) e.Value = firstElement).TakeWhile(Function(g) g.Value <> secondElement))
        swapped.ElementAt(0).AddAfterSelf(middle)
        Return <<%= tab.Name %>>
                   <%= tab.Elements.Select(Function(e) e.ElementsBeforeSelf.Where(Function(f) e.Value = firstElement)) %>
                   <%= swapped %>
                   <%= tab.Elements.Select(Function(e) e.ElementsAfterSelf.Where(Function(f) e.Value = secondElement)) %>
               </>
    End Function
Sub-Main()
尺寸标签=
概述
测试
演出
刹车
Console.WriteLine(SwapElements(“测试”、“性能”选项卡).ToString)
Console.ReadLine()
端接头
函数交换元素(ByVal firstElement作为字符串,ByVal secondElement作为字符串,ByVal tab作为XElement)作为XElement
尺寸交换=制表符元素。其中(函数(e)e.值=第一个元素或e.值=第二个元素)。反向
中间尺寸=制表符元素。选择多个(函数(e)e.ElementsAfterSelf.Where(函数(f)e.Value=firstElement)。TakeWhile(函数(g)g.Value secondElement))
交换的.ElementAt(0).AddAfterSelf(中间)
返回
端函数

我知道这篇文章很旧,但我今天提出了同样的问题,并最终以这种方式解决了它:

private void SwapXNodes(bool up, int inUniqueID)
    {
        XElement currNode = DocumentManager.xMainDocument.XPathSelectElement("//*[@UniqueID='" + inUniqueID + "']"); // find 

        if (up)
        {
            if (currNode.PreviousNode != null)
            {
                XElement xPrevious = new XElement((XElement)currNode.PreviousNode); // copy of previous node

                currNode.PreviousNode.ReplaceWith(currNode); // previous node equal to me
                currNode.ReplaceWith(xPrevious); // Now I should be equal to previous node
            }
        }
        else
        {
            if (currNode.NextNode != null)
            {
                XElement xNext = new XElement((XElement)currNode.NextNode); // copy of Next node

                currNode.NextNode.ReplaceWith(currNode); // Next node equal to me
                currNode.ReplaceWith(xNext); // Now I should be equal to Next node copy
            }
        }
    }

在哪个节点之前/之后移动哪个同级节点的规则是什么?或者你只是在问如何移动节点?这正是我需要的。我需要知道如何移动节点。谢谢,我相信这段代码将测试移到了第一个位置。现在,我将如何移动交换测试和性能?我真的对将节点移动到firat或我只想交换节点的最后一个位置不感兴趣。如何做到这一点?我真的对将节点移动到第一个或最后一个位置不感兴趣,我只是想交换节点。如果当前选项卡正在测试,我将如何交换测试和性能?另外,您的示例假设我知道有多少兄弟姐妹?如果我不知道有多少个同级元素,但我知道测试元素存在于同级元素中的某个位置,我将如何交换这些元素?@Luke101:我添加了一个解决方案,用于交换XDocument中的任意元素。但是我还没有测试过。哇……读了几遍之后,我发现这是解决这个问题最优雅的方法。对不起,我已经选择了答案。我已经实现了你的解决方案。谢谢,如果这两个元素不相邻怎么办?@dtb:那么您需要更多的代码。查询
firstElement
之后的所有内容和
secondElement
之前的所有内容,并执行
。将
添加到
交换的
。我用两行代码更新了上面的代码以反映这一点。
Sub Main()
        Dim tab = <tabs>
                      <tab>
                          <name>Overview</name>
                      </tab>
                      <tab>
                          <name>Testing</name>
                      </tab>
                      <tab>
                          <name>Performance</name>
                      </tab>
                      <tab>
                          <name>Braking</name>
                      </tab>
                  </tabs>
        Console.WriteLine(SwapElements("Testing", "Performance", tab).ToString)
        Console.ReadLine()
    End Sub
    Function SwapElements(ByVal firstElement As String, ByVal secondElement As String, ByVal tab As XElement) As XElement
        Dim swapped = tab.Elements.Where(Function(e) e.Value = firstElement Or e.Value = secondElement).Reverse
        Dim middle = tab.Elements.SelectMany(Function(e) e.ElementsAfterSelf.Where(Function(f) e.Value = firstElement).TakeWhile(Function(g) g.Value <> secondElement))
        swapped.ElementAt(0).AddAfterSelf(middle)
        Return <<%= tab.Name %>>
                   <%= tab.Elements.Select(Function(e) e.ElementsBeforeSelf.Where(Function(f) e.Value = firstElement)) %>
                   <%= swapped %>
                   <%= tab.Elements.Select(Function(e) e.ElementsAfterSelf.Where(Function(f) e.Value = secondElement)) %>
               </>
    End Function
private void SwapXNodes(bool up, int inUniqueID)
    {
        XElement currNode = DocumentManager.xMainDocument.XPathSelectElement("//*[@UniqueID='" + inUniqueID + "']"); // find 

        if (up)
        {
            if (currNode.PreviousNode != null)
            {
                XElement xPrevious = new XElement((XElement)currNode.PreviousNode); // copy of previous node

                currNode.PreviousNode.ReplaceWith(currNode); // previous node equal to me
                currNode.ReplaceWith(xPrevious); // Now I should be equal to previous node
            }
        }
        else
        {
            if (currNode.NextNode != null)
            {
                XElement xNext = new XElement((XElement)currNode.NextNode); // copy of Next node

                currNode.NextNode.ReplaceWith(currNode); // Next node equal to me
                currNode.ReplaceWith(xNext); // Now I should be equal to Next node copy
            }
        }
    }