使用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
? 任何帮助将不胜感激。
答
尝试递归算法
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);
}
}
}
}
答
您可以使用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
的方法,但应该很容易:)
你到目前为止尝试过什么?即使它没有你想要的那么优雅,请给我们展示一些努力。 –