基于文本树创建XML

时间:2014-01-31 21:23:30

标签: c# xml linq tree

我需要从这样的列表中找到:

/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>

如果标签(“这是一个子房间等”)内部的文字是可选的,但是真的很棒!

2 个答案:

答案 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

好的,这是实际工作的地方,让我解释一下规则:

  1. 我们需要循环遍历外部列表,并遍历每个内部列表。
  2. 我们可以使用列表索引来了解要添加的节点或要忽略的名称
  3. 我们需要保持层次结构正确而不是重复节点,而一些XLinq在这里有帮助
  4. 循环 - 请参阅注释以获得详细说明:

    // 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个级别)

    enter image description here

    下面,您将找到包含测试文本等的整个程序,作为一种可行的解决方案:

    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"));