使用LINQ

问题描述:

转换平板XML分层结构,我有一个格式的XML文档:使用LINQ

<root> 
    <node-one> 
     <parent-id /> 
     <node-id>1</node-id> 
     <value>foo</value> 
    </node-one> 
    <node-two> 
     <parent-id>1</parent-id> 
     <node-id>2</node-id> 
     <value>bar</value> 
    </node-two> 
    <node-three> 
     <parent-id>1</parent-id> 
     <node-id>3</node-id> 
     <value>baz</value> 
    </node-three> 
    <node-four> 
     <parent-id>3</parent-id> 
     <node-id>4</node-id> 
     <value>qux</value> 
    </node-four> 
</root> 

我想将其转换为分级树型结构是这样的:

<root> 
    <node-one> 
     <parent-id /> 
     <node-id>1</node-id> 
     <value>foo</value> 
     <node-two> 
     <parent-id>1</parent-id> 
     <node-id>2</node-id> 
     <value>bar</value> 
     </node-two> 
     <node-three> 
     <parent-id>1</parent-id> 
     <node-id>3</node-id> 
     <value>baz</value> 
     <node-four> 
      <parent-id>3</parent-id> 
      <node-id>4</node-id> 
      <value>qux</value> 
     </node-four> 
     </node-three> 
    </node-one> 
</root> 

有没有一种优雅的方式来实现它使用XmlDocument/XDocument? 任何帮助将不胜感激。

+2

你到目前为止尝试过什么?即使它没有你想要的那么优雅,请给我们展示一些努力。 –

尝试递归算法

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml; 
using System.Xml.Linq; 


namespace ConsoleApplication4 
{ 
    class Program 
    { 
     const string FILENAME = @"c:\temp\test.xml"; 
     static List<XElement> nodes; 
     static void Main(string[] args) 
     { 
      XDocument doc = XDocument.Load(FILENAME); 

      nodes = doc.Root.Elements().ToList(); 

      XElement parent = new XElement("root"); 
      RecursvieAdd(parent, ""); 
      XDocument doc2 = new XDocument(parent); 
     } 
     static void RecursvieAdd(XElement parent, string parentId) 
     { 
      foreach(XElement child in nodes.Where(x => (string)x.Element("parent-id") == parentId)) 
      { 
       XElement newChild = new XElement(child); 
       parent.Add(newChild); 
       string id = (string)child.Element("node-id"); 
       RecursvieAdd(newChild, id); 
      } 

     } 
    } 

} 
+0

谢谢,但它不起作用。 'node-two'和'node-three'应该是兄弟姐妹,两者的直接父母应该是'node-one' – koryakinp

+0

修正了,我没有很好地看你的样本输出。在过去做了很多次。 – jdweng

+0

干净优雅,谢谢先生。 – koryakinp

您可以使用XPath的或LINQ来完成这件事。由于我最近使用XPath,我会告诉你如何去这样做。

(您可能需要一些引用添加到的System.Xml在参考浏览器)

入住此示例代码(包括提示注释):

using System; 
using System.Linq; 
using System.Xml.Linq; 
using System.Xml.XPath; 

namespace xmlTest 
{ 
    internal class Program 
    { 
     public static void Main(string[] args) 
     { 
      var document = CreateDocument(); 

      // Gets all children of the root element whose name starts with "node". 
      var nodeElements = document.XPathSelectElements("/root/*[starts-with(name(), 'node')]").ToList(); 
      // Creates Tuples in the fashion: { 1, $NODE_ONE }, { 2, $NODE_TWO }, ... 
      // This is done because some values might be skipped. 
      var indexedNodes = nodeElements.Select(x => new Tuple<int, XElement>(int.Parse(x.Descendants("node-id").First().Value), x)).ToList(); 

      foreach(var indexedNode in indexedNodes) 
      { 
       var parentId = GetParentNodeId(indexedNode.Item2); 
       if (parentId != null) 
       { 
        // Remove the node from its parent. 
        indexedNode.Item2.Remove(); 
        // Add the node to the new parent. 
        var newParent = indexedNodes.First(x => x.Item1 == parentId).Item2; 
        newParent.Add(indexedNode.Item2); 
       } 
      } 
      Console.WriteLine(document.ToString()); 
     } 

     static int? GetParentNodeId(XElement element) { 
      try { 
       var parentId = int.Parse(element.Descendants("parent-id").First().Value); 
       return parentId; 
      } 
      catch // Add some appropriate error handling here. 
      { 
       return null; 
      } 
     } 

     private static XDocument CreateDocument() 
     { 
      const string xml = 
       "<root> <node-one> <parent-id /> <node-id>1</node-id> <value>foo</value> </node-one> <node-two> <parent-id>1</parent-id> <node-id>2</node-id> <value>bar</value> </node-two> <node-three> <parent-id>1</parent-id> <node-id>3</node-id> <value>baz</value> </node-three> <node-four> <parent-id>3</parent-id> <node-id>4</node-id> <value>qux</value> </node-four> </root>"; 
      return XDocument.Parse(xml); 
     } 
    } 
} 

你可以从看输出对元素所做的所有更改都已反映在XDocument中。这是因为在使用X-Class时,所有改变都已经到位。

编辑:您需要更改一些代码以将其包含到您的应用程序中,例如, CreateDocument的方法,但应该很容易:)