Click here to Skip to main content
15,886,821 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I am being assigned with the task to convert plain text file into xml file using C#.the plain text file contains:
##Main
MachineName DomainName Scandate GUID RegID
RMM-LT-417@*@Home.LOCAL@*@03/23/2015 18:48:38@*@31c0841e-f7bf-4de1-9d75-7e9080498e6b-20141216020243430495@*@2853625

##AP_LogicalDrivesInformation
Caption Description DriveType FileSystem FreeSpace UsedSpace Size CFreeSpace CUsedSpace VolumeName VolumeSerialNumber
C@*@Hard Drive@*@Drive Fixed@*@NTFS@*@421002715136@*@79097778176@*@500100493312@*@392.1 GB@*@73.7 GB@*@OS@*@469D6D66
D@*@Hard Drive@*@Drive Fixed@*@NTFS@*@484706508800@*@4765618176@*@489472126976@*@451.4 GB@*@4.4 GB@*@DATAPART@*@7A9EDCCC
E@*@MATSHITA DVD+-RW UJ8E2@*@Cd-Rom@*@@*@0@*@0@*@0@*@0@*@0@*@@*@0

where ####Main and ##AP_LogicalDrivesInformation are root element
MachineName DomainName Scandate GUID RegID are nodes and line below that are its value which splits line after @*@.

My code for the same is :
C#
var xml = new StringBuilder();

xml.Append("<Main>\n");

foreach (var line in File.ReadAllLines(@"yourfile.txt"))
{
    Regex.Replace(line, @"(?<=(?:\r?\n){2}|\A)(?:\r?\n)+", "");

    var line1 = line.Replace("@*@", ";");

    var vals = line1.Split(';');

    // TODO add more fields
    xml.AppendFormat("<MachineName>{0}</MachineName>\n <DomainName>{1}</DomainName>\n <Scandate>{2}</Scandate>\n <GUID>{3}</GUID>\n <RegID>{4}</RegID>\n",
                   vals[0].Trim(), vals[1].Trim(), vals[2].Trim(), vals[3].Trim(), vals[4].Trim());
    xml.Append("</Main>\n");
}

But the problem now is no new root node is created for ##AP_LogicalDrivesInformation
Posted
Updated 30-Mar-15 15:45pm
v3
Comments
Stephen Hewison 24-Mar-15 8:58am    
Looks like the following is inside your look where the opening tag is outside at the start:

xml.Append("</Main>\n");
Richard Deeming 24-Mar-15 9:06am    
An XML file cannot contain multiple root nodes.
Maciej Los 30-Mar-15 9:03am    
Does a structure of text file is always the same? I mean: 5 elements for Main and 11 for DrivesInformation?

You need just to check your logic, nothing else. XML is allowed to have only one top element, root (credit to Richard Deeming).

I would not use StringBuilder though. I would use string.Format (for very rigid-format XML schema only) or, even better, System.Xml.XmlWriter:
https://msdn.microsoft.com/en-us/library/system.xml.xmlwriter%28v=vs.110%29.aspx[^].

—SA
 
Share this answer
 
This is one possible solution, however if it is exactly what you want is difficult for me to say with the little information you have provided.
The resulting XML structure is just a guesstimate and might not suit your needs.

First read the whole contents of the file into a string.
(If the file is very big, this approach might not be the best)
C#
string content = File.ReadAllText(<filename>);</filename>

Then use this regular expression on the content:
C#
Regex expression = new Regex(@"##(?<parent>\w+)\r\n(?<children>[\w ]+)\r\n((?<values>[\S ]+)(\r\n(?!#)|$))+", RegexOptions.None);

This expression will give you three named groups where of the 'values' group has one or more captures.
Then loop through all matches and create the XML structure.
I have used XElement, but XmlDocument can also be used.
C#
XElement xeRoot = new XElement("root");   // Change the name 'root' to whatever suitable
foreach (Match m in expression.Matches(content))
{
    XElement xeParent = new XElement(m.Groups["parent"].Value);

    string[] children = m.Groups["children"].Value.Split(' ');      // Use space as delimter for the children
    
    foreach (Capture cap in m.Groups["values"].Captures)
    {
        XElement xeChild = new XElement("child");   // Change the name 'child' to whatever suitable
        string[] values = cap.Value.Split(new string[] { "@*@" }, StringSplitOptions.None);

        // Check that the children and values counts are equal
        if (children.Length != values.Length)
            throw new Exception("The number of children and values mismatch.");
      
        for (int i = 0; i < children.Length; i++)
        {
            XElement xeChildValue = new XElement(children[i]);
            xeChildValue.Value = values[i];
            xeChild.Add(xeChildValue);
        }

        xeParent.Add(xeChild);
    }
    
    xeRoot.Add(xeParent);
}

Finally save the XML data to file
C#
XDocument doc = new XDocument();
doc.Add(xeRoot);
doc.Save(@"C:\Temp\test.xml");


Resulting XML
XML
<?xml version="1.0" encoding="utf-8"?>
<root>
  <Main>
    <child>
      <MachineName>RMM-LT-417</MachineName>
      <DomainName>Home.LOCAL</DomainName>
      <Scandate>03/23/2015 18:48:38</Scandate>
      <GUID>31c0841e-f7bf-4de1-9d75-7e9080498e6b-20141216020243430495</GUID>
      <RegID>2853625</RegID>
    </child>
  </Main>
  <AP_LogicalDrivesInformation>
    <child>
      <Caption>C</Caption>
      <Description>Hard Drive</Description>
      <DriveType>Drive Fixed</DriveType>
      <FileSystem>NTFS</FileSystem>
      <FreeSpace>421002715136</FreeSpace>
      <UsedSpace>79097778176</UsedSpace>
      <Size>500100493312</Size>
      <CFreeSpace>392.1 GB</CFreeSpace>
      <CUsedSpace>73.7 GB</CUsedSpace>
      <VolumeName>OS</VolumeName>
      <VolumeSerialNumber>469D6D66</VolumeSerialNumber>
    </child>
    <child>
      <Caption>D</Caption>
      <Description>Hard Drive</Description>
      <DriveType>Drive Fixed</DriveType>
      <FileSystem>NTFS</FileSystem>
      <FreeSpace>484706508800</FreeSpace>
      <UsedSpace>4765618176</UsedSpace>
      <Size>489472126976</Size>
      <CFreeSpace>451.4 GB</CFreeSpace>
      <CUsedSpace>4.4 GB</CUsedSpace>
      <VolumeName>DATAPART</VolumeName>
      <VolumeSerialNumber>7A9EDCCC</VolumeSerialNumber>
    </child>
    <child>
      <Caption>E</Caption>
      <Description>MATSHITA DVD+-RW UJ8E2</Description>
      <DriveType>Cd-Rom</DriveType>
      <FileSystem></FileSystem>
      <FreeSpace>0</FreeSpace>
      <UsedSpace>0</UsedSpace>
      <Size>0</Size>
      <CFreeSpace>0</CFreeSpace>
      <CUsedSpace>0</CUsedSpace>
      <VolumeName></VolumeName>
      <VolumeSerialNumber>0</VolumeSerialNumber>
    </child>
  </AP_LogicalDrivesInformation>
</root>
 
Share this answer
 
v3
Comments
Maciej Los 31-Mar-15 8:21am    
Bravo!
+5!
George Jonsson 31-Mar-15 9:00am    
Thanks, Maciej
Maciej Los 31-Mar-15 9:27am    
I was wondering about linq solution, but haven't enough time to write it.
Very good job, George ;)
George Jonsson 31-Mar-15 9:30am    
Thanks again.
There is probably a way to do it in Linq, but I have to admit I am not so good with that technology yet. Still mixing up Select and Where all the time,
Maciej Los 31-Mar-15 9:32am    
;)Linq is very powerful "tool" and i'm still learning it...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900