我需要从这样的列表中找到:
/home
/home/room1
/home/room1/subroom
/home/room2
/home/room2/miniroom
/home/room2/bigroom
/home/room2/hugeroom
/home/room3
到xml文件。我已经尝试使用LINQ to XML来做到这一点,但我最终感到困惑,不知道该怎么做。非常感谢任何帮助!
编辑:
我希望XML文件看起来像这样:
<home>
<room1>
<subroom>This is a subroom</subroom>
</room1>
<room2>
<miniroom>This is a miniroom</miniroom>
<bigroom>This is a bigroom</bigroom>
<hugeroom>This is a hugeroom</hugeroom>
</room2>
<room3></room3>
</home>
如果标签(“这是一个子房间等”)内部的文字是可选的,但是真的很棒!
答案 0 :(得分:4)
好的伙计,这是一个解决方案。
一些笔记和解释。
您的文本结构可以拆分成行,然后再用斜杠分成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()
来首先将文本分成行,然后分成每行的部分,最后得到一个多维数组样式{{1} }包含List<T>
个对象,在本例中为List<T>
。
首先让我们创建容器对象:
List<List<string>>
接下来,我们应该拆分线。让我们调用包含文本的变量“text”。
var possibleNodes = new List<List<string>>();
这给了我们一个列表,但我们的行仍然没有被分解。让我们用斜杠(var splitLines = text
.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
)字符再次拆分它们。这是我们构建节点名称的地方。我们可以在ForEach中执行此操作,只需添加到可能的节点列表中:
/
现在,我们需要知道XML的DEPTH。您的文字显示将有3个深度节点。节点深度是任何一个给定节点行的最大深度,现在存储在splitLines.ForEach(l =>
possibleNodes.Add(l
.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.ToList()
)
);
;我们可以使用List<List<string>>
方法来获取此信息:
.Max()
最后的设置步骤:我们不需要第一行,因为它只是“home”而且它将是我们的根节点。我们可以创建一个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);
此处的奖励是此代码可以与任意数量的节点一起使用并构建您的XML。
要检查它,// 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));
}
}
}
}
}
}
有一个漂亮的XDocument
覆盖实现,只是吐出它所持有的所有XML,所以你要做的就是:
.ToString()
而且,你会得到这个结果: (注意我添加了一个测试节点,以确保它可以使用超过3个级别)
下面,您将找到包含测试文本等的整个程序,作为一种可行的解决方案:
Console.Write(xDoc.ToString());
答案 1 :(得分:3)
LINQ魔术的时间?
// load file into string[]
var input = File.ReadAllLines("TextFile1.txt");
// in case you have more than one home in your file
var homes =
new XDocument(
new XElement("root",
from line in input
let items = line.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)
group items by items[0] into g
select new XElement(g.Key,
from rooms in g.OrderBy(x => x.Length).Skip(1)
group rooms by rooms[1] into g2
select new XElement(g2.Key,
from name in g2.OrderBy(x => x.Length).Skip(1)
select new XElement(name[2], string.Format("This is a {0}", name[2]))))));
// get the right home
var home = new XDocument(homes.Root.Element("home"));