好的,伙计,这是一个解决方案。
一些注释和解释。
您的文本结构可以分成几行,然后再用斜杠分割成 XML 节点的名称。如果你以这种方式思考文本,你会得到一个“行”列表,分解成一个列表
名字。
/home
首先,第一行/home是XML的根;我们可以摆脱它,只创建一个以该名称作为根元素的 XDocument 对象;
var xDoc = new XDocument("home");
当然我们不想硬编码,但这只是一个例子。现在,开始真正的工作:
/home/room1/
/home/room1/bigroom
etc...
作为List<T>,它看起来像这样
myList = new List<List<string>>();
... [ add the items ]
myList[0][0] = home
myList[0][1] = room1
myList[1][0] = home
myList[1][1] = room1
myList[1][2] = bigroom
所以我们可以做的就是使用string.Split()多次将你的文本分成几行,然后分成每行的部分,最后得到一个包含@的多维数组样式List<T> 987654330@ 对象,在本例中为 List<List<string>>。
首先让我们创建容器对象:
var possibleNodes = new List<List<string>>();
接下来,我们应该拆分行。我们将保存文本的变量称为“文本”。
var splitLines = text
.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
这给了我们一个列表,但我们的行仍然没有分解。让我们用斜杠 (/) 字符再次拆分它们。这是我们构建节点名称的地方。我们可以在 ForEach 中执行此操作,然后将其添加到我们的可能节点列表中:
splitLines.ForEach(l =>
possibleNodes.Add(l
.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.ToList()
)
);
现在,我们需要知道 XML 的深度。您的文字显示将有 3 个深度节点。节点深度是任何一个给定行节点的最大深度,现在存储在List<List<string>>;我们可以使用.Max() 方法得到这个:
var nodeDepth = possibleNodes.Max(n => n.Count);
最后的设置步骤:我们不需要第一行,因为它只是“家”,它将是我们的根节点。我们可以只创建一个XDocument 对象并将其第一行作为Root 的名称:
// Create the root node
XDocument xDoc = new XDocument(new XElement(possibleNodes[0][0]));
// We don't need it anymore
possibleNodes.RemoveAt(0);
好的,这里是真正的工作发生的地方,让我解释一下规则:
- 我们需要遍历外部列表和每个内部列表。
- 我们可以使用列表索引来了解要添加到哪个节点或要忽略哪些名称
- 我们需要保持适当的层次结构,而不是重复节点,一些 Xlinq 在这里可以提供帮助
循环 - 有关详细说明,请参阅 cmets:
// This gets us looping through the outer nodes
for (var i = 0; i < possibleNodes.Count; i++)
{
// Here we go "sideways" by going through each inner list (each broken down line of the text)
for (var ii = 1; ii < nodeDepth; ii++)
{
// Some lines have more depth than others, so we have to check this here since we are looping on the maximum
if (ii < possibleNodes[i].Count)
{
// Let's see if this node already exists
var existingNode = xDoc.Root.Descendants().FirstOrDefault(d => d.Name.LocalName == (possibleNodes[i][ii]));
// Let's also see if a parent node was created in the previous loop iteration.
// This will tell us whether to add the current node at the root level, or under another node
var parentNode = xDoc.Root.Descendants().FirstOrDefault(d => d.Name.LocalName == (possibleNodes[i][ii - 1]));
// If the current node has already been added, we do nothing (this if statement is not entered into)
// Otherwise, existingNode will be null and that means we need to add the current node
if (null == existingNode)
{
// Now, use parentNode to decide where to add the current node
if (null == parentNode)
{
// The parent node does not exist; therefore, the current node will be added to the root node.
xDoc.Root.Add(new XElement(possibleNodes[i][ii]));
}
else
{
// There IS a parent node for this node!
// Therefore, we must add the current node to the parent node
// (remember, parent node is the previous iteration of the inner for loop on nodeDepth )
var newNode = new XElement(possibleNodes[i][ii]);
parentNode.Add(newNode);
// Add "this is a" text (bonus!) -- only adding this text if the current node is the last one in the list.
if (possibleNodes[i].Count -1 == ii)
{
newNode.Add(new XText("This is a " + newNode.Name.LocalName));
}
}
}
}
}
}
这里的好处是这段代码可以使用任意数量的节点并构建您的 XML。
要检查它,XDocument 有一个漂亮的 .ToString() 覆盖实现,它只会吐出它持有的所有 XML,所以你要做的就是:
Console.Write(xDoc.ToString());
而且,您会得到以下结果:
(注意我添加了一个测试节点以确保它适用于超过 3 个级别)
您将在下面找到整个程序以及您的测试文本等,作为一个可行的解决方案:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace XmlFromTextString
{
class Program
{
static void Main(string[] args)
{
// This simulates text from a file; note that it must be flush to the left of the screen or else the extra spaces
// add unneeded nodes to the lists that are generated; for simplicity of code, I chose not to implement clean-up of that and just
// ensure that the string literal is not indented from the left of the Visual Studio screen.
string text =
@"/home
/home/room1
/home/room1/subroom
/home/room2
/home/room2/miniroom
/home/room2/test/thetest
/home/room2/bigroom
/home/room2/hugeroom
/home/room3";
var possibleNodes = new List<List<string>>();
var splitLines = text
.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
splitLines.ForEach(l =>
possibleNodes.Add(l
.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.ToList()
)
);
var nodeDepth = possibleNodes.Max(n => n.Count);
// Create the root node
XDocument xDoc = new XDocument(new XElement(possibleNodes[0][0]));
// We don't need it anymore
possibleNodes.RemoveAt(0);
// This gets us looping through the outer nodes
for (var i = 0; i < possibleNodes.Count; i++)
{
// Here we go "sideways" by going through each inner list (each broken down line of the text)
for (var ii = 1; ii < nodeDepth; ii++)
{
// Some lines have more depth than others, so we have to check this here since we are looping on the maximum
if (ii < possibleNodes[i].Count)
{
// Let's see if this node already exists
var existingNode = xDoc.Root.Descendants().FirstOrDefault(d => d.Name.LocalName == (possibleNodes[i][ii]));
// Let's also see if a parent node was created in the previous loop iteration.
// This will tell us whether to add the current node at the root level, or under another node
var parentNode = xDoc.Root.Descendants().FirstOrDefault(d => d.Name.LocalName == (possibleNodes[i][ii - 1]));
// If the current node has already been added, we do nothing (this if statement is not entered into)
// Otherwise, existingNode will be null and that means we need to add the current node
if (null == existingNode)
{
// Now, use parentNode to decide where to add the current node
if (null == parentNode)
{
// The parent node does not exist; therefore, the current node will be added to the root node.
xDoc.Root.Add(new XElement(possibleNodes[i][ii]));
}
else
{
// There IS a parent node for this node!
// Therefore, we must add the current node to the parent node
// (remember, parent node is the previous iteration of the inner for loop on nodeDepth )
var newNode = new XElement(possibleNodes[i][ii]);
parentNode.Add(newNode);
// Add "this is a" text (bonus!) -- only adding this text if the current node is the last one in the list.
if (possibleNodes[i].Count -1 == ii)
{
newNode.Add(new XText("This is a " + newNode.Name.LocalName));
// For the same default text on all child-less nodes, us this:
// newNode.Add(new XText("This is default text"));
}
}
}
}
}
}
Console.Write(xDoc.ToString());
Console.ReadKey();
}
}
}