Reputation: 13
I have a text file with the following lines:
root_node1_node2_node3=value
root_node1_node2_node3=value
root_node1_node2_node3_node4=value
root_node5_node6=value
What I would like to get:
<root>
<node1>
<node2>
<node3>value</node3>
<node3>value</node3>
<node3>
<node4>value</node4>
</node3>
</node2>
</node1>
<node5>
<node6>value</node6>
</node5>
</root>
So I would like to convert the lines of varied lengths to XML nodes, then merge them to a single XML file in C#.
I read about LINQtoXML but I'm still new to it.
Please help.
This is where I am now:
// file
string fileName = Path.GetFileName(file);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
string fileSourceDirectory = Path.GetDirectoryName(file);
// xml
XDocument xDoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
// root
new XElement(fileNameWithoutExtension)
);
try
{
using(StreamReader sr = new StreamReader(file))
{
string line;
// go line-by-line
while((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
// element=value -> [element, value]
string[] elementAndValue = line.Split('=');
// elment_element_element -> [element, elelement, element]
string[] elements = elementAndValue[0].Split('_');
// value
string value = elementAndValue[1];
List<XElement> elementList = new List<XElement>();
for(int i = 0; i < elements.Length; i++)
{
if(i == (elements.Length - 1))
{
elementList.Add(new XElement(elements[i], value));
Console.WriteLine("Added: " + elements[i] + "=" + value);
}
else
{
elementList.Add(new XElement(elements[i]));
Console.WriteLine("Added: " + elements[i]);
}
}
xDoc.Root.Add(elementList[0]);
Console.WriteLine("Added first item to root.");
for(int i = 0; i < elementList.Count - 1; i++)
{
elementList[i].Add(elementList[i + 1]);
Console.WriteLine("Added " + elementList[i + 1] + " to " + elementList[i]);
}
}
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
// save xml
xDoc.Save(outputDir + "\\" + fileNameWithoutExtension + ".xml");
This does the first part what I wanted.
The output is something like this now:
<root>
<node1>
<node2>
<node3>value</node3>
</node2>
</node1>
<node1>
<node2>
<node3>value</node3>
</node2>
</node1>
<node1>
<node2>
<node3>
<node4>value</node4>
</node3>
</node2>
</node1>
<node5>
<node6>value</node6>
</node5>
</root>
Now I would like to merge these nodes to the format which I described the first place.
Thank you for any help. :)
Upvotes: 1
Views: 577
Reputation: 24144
One more implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Ini2XML
{
class Program
{
static void Main(string[] args)
{
string[] lines = new string[] {
"root_node1_node2_node3 = value",
"root_node1_node2_node3 = value1",
"root_node1_node2_node3_node4 = value",
"root_node5_node6 = value",
"root_node5_node6_node7_node8_node8 = value",
"root_node55 = value2 2232 2 232 2 2 "
};
XDocument xml = Ini2XMLConverter.ConvertStringArrayToXML(lines);
xml.Save(Console.Out);
}
public class Ini2XMLConverter
{
public static XDocument ConvertStringArrayToXML(IEnumerable<string> lines)
{
if (lines == null) { return null; };
XDocument xml = new XDocument();
foreach (string s in lines)
{
//define path and value from each line
string[] sp = s.Split('=');
string[] path = sp[0].Trim().Split('_');
string value = sp[1].Trim();
//node to attach the current node
XElement attachTo = null;
for (int i = 0; i < path.Length; i++)
{
XElement currentNode = null;
if (i == path.Length - 1)
{ //last node it's a TEXT node with value after "="
currentNode = new XElement(path[i], value);
}
else
{ //a simple node in the middle
currentNode = new XElement(path[i]);
}
// adding the root
if (xml.Root == null)
{
xml.Add(currentNode);
attachTo = currentNode;
continue;
}
else if (attachTo == null)
{ //If it's a root then the first elements have to be the same
attachTo = xml.Root;
continue;
}
//looking for the same name node
XElement f = attachTo.Descendants(currentNode.Name).Count() == 0 ? null : attachTo.Descendants(currentNode.Name).First();
//skip elements with TEXT values
if ((f != null) && (f.LastNode != null) && (f.LastNode.NodeType == System.Xml.XmlNodeType.Text)) { f = null; }
if (f == null)
{
//add new node
attachTo.Add(currentNode);
attachTo = currentNode;
}
else
{ //don't add node if it exists already. Just move pointer to it.
attachTo = f;
}
}
}
return xml;
}
}
}
}
Output:
<root>
<node1>
<node2>
<node3>value</node3>
<node3>value1</node3>
<node3>
<node4>value</node4>
</node3>
</node2>
</node1>
<node5>
<node6>value</node6>
<node6>
<node7>
<node8>
<node8>value</node8>
</node8>
</node7>
</node6>
</node5>
<node55>value2 2232 2 232 2 2</node55>
</root>
Upvotes: 0
Reputation: 4119
try this,
XDocument xDoc = new XDocument();
while ((line = sr.ReadLine()) != null)
{
string[] nodes = line.Split('_');
for (int j = 0; j < nodes.Length; j++)
{
if(j == 0) // assume that all line should start with same root name
{
if (xDoc.Root == null)
{
var root = new XElement(nodes[j]);
xDoc.Add(root);
}
}
else
{
var previousNode = xDoc.Descendants(nodes[j - 1]).FirstOrDefault();
if (nodes[j].Contains('='))
{
var elementValues = nodes[j].Split('=');
if (previousNode.DescendantNodes().Count() == 1 && previousNode.Value != "")
{
previousNode.AddAfterSelf(new XElement(nodes[j - 1], new XElement(elementValues[0], elementValues[1])));
}
else
{
previousNode.Add(new XElement(elementValues[0], elementValues[1]));
}
}
else
{
var node = xDoc.Descendants(nodes[j]);
if (node.Count() == 0)
{
previousNode.Add(new XElement(nodes[j]));
}
}
}
}
Workable solution : .NET Fiddle
Upvotes: 1
Reputation: 41
Hi Please check this link which is useful for what your looking for
http://www.dreamincode.net/forums/topic/218979-linq-to-xml/
Upvotes: 0