I'm working on a project that requires that I take data from an XML API that I don't control and insert that data into a database (I control) for consumption by a different application (I control but can't modify code too it)
We already managed to get the full database layout from the XML API via the providers special endpoint but all other queries (to get data) are via XML.
Currently what I have is the following:
Book.cs - This represents one of the database tables
using System;
using System.ComponentModel.DataAnnotations;
namespace Example.Models
{
public partial class Book
{
[Key]
public int Recordno { get; set; }
public decimal? Amount { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? Whenmodified { get; set; }
public DateTime? Whencreated { get; set; }
public int? Createdby { get; set; }
public int? Modifiedby { get; set; }
}
}
API Return XML - This represents the XML returned by the API (an SDK already converts it to an XMLDocument Type)
<BOOKS>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is a title</TITLE>
<DESCRIPTION>This is a description</DESCRIPTION>
<WHENMODIFIED></WHENMODIFIED>
<WHENCREATED>13/03/20 15:23:12</WHENCREATED>
<CREATEDBY>3</CREATEDBY>
<MODIFIEDBY></MODIFIEDBY>
</BOOK>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is another title</TITLE>
<DESCRIPTION>This is another description</DESCRIPTION>
<WHENMODIFIED>21/03/20 12:45:23</WHENMODIFIED>
<WHENCREATED>15/03/20 15:23:12</WHENCREATED>
<CREATEDBY>6</CREATEDBY>
<MODIFIEDBY>2</MODIFIEDBY>
</BOOK>
</BOOKS>
Currently the way we are doing this is via switch statements, but I'm hoping that there is a better method.
Partial BookController.cs
var books = new List<Book>();
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var xElements = result.Elements();
var book = new Book();
foreach (var element in xElements)
{
switch (element.Name.ToString())
{
case "RECORDNO":
book.Recordno = int.Parse(element.Value);
break;
case "AMOUNT":
if (element.Value != string.Empty)
{
book.Amount = int.Parse(element.Value);
}
break;
// etc.
}
}
books.Add(book);
}
I have a feeling that there is a much easier method of doing this that I'm just unaware of, my concern is that I have about fifty tables and hundreds of elements to do making this method inefficient time wise.
Use xml serialization
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication8
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Books));
Books books = (Books)serializer.Deserialize(reader);
}
}
[XmlRoot("BOOKS")]
public class Books
{
[XmlElement("BOOK")]
public List<Book> books { get; set; }
}
public partial class Book
{
//[Key]
[XmlElement("RECORDNO")]
public int Recordno { get; set; }
[XmlElement("AMOUNT")]
public decimal? Amount { get; set; }
[XmlElement("TITLE")]
public string Title { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
private DateTime Whenmodified { get;set;}
[XmlElement("WHENMODIFIED")]
public string _Whenmodified {
get { return Whenmodified.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value,"dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);}
}
private DateTime Whencreated { get; set; }
public string _Whencreated
{
get { return Whencreated.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value, "dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); }
}
[XmlElement("CREATEDBY")]
public int? Createdby { get; set; }
[XmlElement("MODIFIEDBY")]
public int? Modifiedby { get; set; }
}
}
You could do the following:
use XML Schema Definition Tool (Xsd.exe) to generate classes
use generated classes to read the input XML
use that data
Welcome to SO 👋
What you're trying to do here of construct an in-memory C# object from an XML string representation is known as deserialization. There are lots of resources on this (e.g. MSDN), but here's a link to a SO post where the OP was looking to achieve something similar.
Applied to your above code snippet might look something like this:
var books = new List<Book>();
var serializer = new XmlSerializer(typeof(Book));
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var book = (Book)serializer.Deserialize(result.CreateReader());
books.Add(book);
}
I need to parse a xml document into object models that I've created but I can't figure out how to do so, I think it's because of my lack of understanding of the xml structure.
I've tried to get all the elements from the document and to create individual object from each based on their attributes I think they're called.
Here is my C# code :
var manifest = XDocument.Load(theDocument);
var allTheElements = manifest.Descendants();
foreach (var element in allTheElements)
{
//No idea how to parse each object into individual ManifestModel's
}
public class ManifestModel
{
public string Version { get; set; }
public string Resource { get; set; }
public string Size { get; set; }
public string Checksum { get; set; }
}
And here is the XML data :
<?xml version="1.0" encoding="UTF-8"?>
<manifest version="1.0.0" totalbytes="6131797">
<source uri="codeapi.io/Game/patches/">
<file resource="FooGame.sln" size="1125" checksum="530B9F1C2412A6D74EF017919074FD8966E5280D" />
<file resource=".vs\FooGame\v16\.suo" size="69120" checksum="438976A3681FDD503DB4FBFCBB5D420E9E8838DD" />
</source>
</manifest>
Just like we have json2csharp for JSON, we have Xml2Csharp for XML. There are probably lots of other sites that will do this.
Paste your XML and it generates this:
[XmlRoot(ElementName="file")]
public class File {
[XmlAttribute(AttributeName="resource")]
public string Resource { get; set; }
[XmlAttribute(AttributeName="size")]
public string Size { get; set; }
[XmlAttribute(AttributeName="checksum")]
public string Checksum { get; set; }
}
[XmlRoot(ElementName="source")]
public class Source {
[XmlElement(ElementName="file")]
public List<File> File { get; set; }
[XmlAttribute(AttributeName="uri")]
public string Uri { get; set; }
}
[XmlRoot(ElementName="manifest")]
public class Manifest {
[XmlElement(ElementName="source")]
public Source Source { get; set; }
[XmlAttribute(AttributeName="version")]
public string Version { get; set; }
[XmlAttribute(AttributeName="totalbytes")]
public string Totalbytes { get; set; }
}
One could call that lazy or cheating, but I don't see the point in writing code that can be generated for me in a second. You might not always get perfect results, but it's a good starting point. For example, it uses string for all attribute types. If you're expecting all numeric values you could replace those with int or long.
Now you can deserialize like this:
var serializer = new XmlSerializer(typeof(Manifest), new XmlRootAttribute("manifest"));
using (var stream = System.IO.File.OpenRead("test.xml"))
{
var deserialized = (Manifest)serializer.Deserialize(stream);
}
Once you've got the data deserialized into something, the rest is much easier. You can either use the auto-generated models or map them to your own.
Using LINQ...
c#
void Main()
{
string fileName = #"e:\Temp\GamePatches.xml";
XDocument manifest = XDocument.Load(fileName);
string version = manifest.Root.Attribute("version").Value;
List<ManifestModel> manifestModel = new List<ManifestModel>();
foreach (XElement e in manifest.Descendants("file"))
{
manifestModel.Add(new ManifestModel() { Version = version
, Resource = (string)e.Attribute("resource").Value
, Size = (string)e.Attribute("size").Value
, Checksum = (string)e.Attribute("checksum").Value }
);
}
}
// Define other methods and classes here
public class ManifestModel
{
public string Version { get; set; }
public string Resource { get; set; }
public string Size { get; set; }
public string Checksum { get; set; }
}
I spent a lot of time working on a similar app that parsed through XML Schema, and I found the easiest way is to turn the XML Document into an XmlNodeList. From here you can use the SelectNodes and SelectSingleNodes to navigate through it. Take a look at this:https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlnode.selectnodes?view=netframework-4.8, but basically what you do is create an xpath string which selects the node you need. Here is some documentation on that: https://learn.microsoft.com/en-us/dotnet/standard/data/xml/select-nodes-using-xpath-navigation
Sorry for the somewhat basic question, but what can I say. I can't figure it out. The problem is that there's a foreach loop that's supposed to iterate through the rows (sections) and while it works for the first section, the second time through the loop it doesn't seem to read the second section. The same data is stored in version. BTW, the way the method is called I would be passing in ProductName as a parameter (There will be multiple products represented here and also a version number (e.g. v2.0.0) that I'll need to filter the results for too.
So I have an XML file that looks like this:
<Products>
<ProductName1>
<v2.0.0>
<GUID>"{B5ECEC43-5406-4E4D-96D9-456823100313}"</GUID>
<VersionNameToUninstall>"2.0.0 - 2.0.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.0-Uninst.iss"</UninstallResponseFile>
</v2.0.0>
<v2.0.3>
<GUID>"{1D6C02D7-8E87-43BE-8AB2-1FF0E5ACD410}"</GUID>
<VersionNameToUninstall>"2.0.3"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.3-Uninst.iss"</UninstallResponseFile>
</v2.0.3>
</ProductName1>
<ProductName2>
<v3.0.0>
<GUID>"{ABCDEC43-5406-4E4D-96D9-456823101234}"</GUID>
<VersionNameToUninstall>"2.2.0 - 2.2.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.2.0-Uninst.iss"</UninstallResponseFile>
</v3.0.0>
<v4.0.0>
<GUID>"{5D6C02D7-8E87-43BE-8AB2-1FF0E5ACD589}"</GUID>
<VersionNameToUninstall>"4.0.0"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-4.0.0-Uninst.iss"</UninstallResponseFile>
</v4.0.0>
</ProductName2>
</Products>
There will only be 10 or so versions (e.g. v2.x.x) so there's not a lot of data here. So I created a multidimensional (nested) class/struct to hold the data and when I try my code to read the data it's not working.
Here are the classes/stucts (I've tried both and neither works) that I'm trying to populate:
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row {get;set;}
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
So here's my code. Please just ignore the Stream - that's so I can embed this XML file in the .exe and not have it be a separate file:
public static List<TopLevelObject> GetGUIDSFromFile(string GUIDKey)
List<InstallScriptMSIXMLTopLevelObject> installScriptMSIXMLTopLevelObjectList = new List<InstallScriptMSIXMLTopLevelObject>();
Stream GUIDXmlFileStream = typeof(PGCommonCA).Assembly.GetManifestResourceStream("PGCommonCA.ProductGUIDs.xml");
XElement xElement = XElement.Load(GUIDXmlFileStream);
var versions = xElement.Elements(GUIDKey).Descendants();
foreach (var version in versions)
{
TopLevelObject topLevelObject = new TopLevelObject();
RowLevelObject rowLevelObject = new RowLevelObject();
TopLevelObject.Version = version.Name.LocalName;
RowLevelObject.Guid = version.Element("GUID").Value;
RowLevelObject.VersionName = version.Element("VersionNameToUninstall").Value;
RowLevelObject.UninstallFileName = version.Element("UninstallResponseFile").Value;
TopLevelObjectList.Add(topLevelObject);
}
return TopLevelObjectList;
}
I know there are many ways to read XML and my choice doesn't work so I'm looking for another simple solution.
The following works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}
I figured it out (many thanks to jdweng!!). Here's the final solution based on the revised XML at the top:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static TopLevelObject GetInfo(string xmlKey)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Descendants(xmlKey).Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}
Below is the json text I receive for a test web service and looking for how it each record field could be displayed .
{"records":[
{"id":"10","email":"bcomecomaaacomea#myhost.om","name":"Dot"},{"id":"855","email":"webcastpoa0#myhost","name":"name_0"},{"id":"856","email":"webcastpoa1#myhost","name":"name_1"},{"id":"857","email":"webcastpoa2#myhost","name":"name_2"},{"id":"858","email":"webcastpoa3#myhost","name":"name_3"},{"id":"859","email":"webcastpoa4#myhost","name":"name_4"},{"id":"860","email":"webcastpoa5#myhost","name":"name_5"},{"id":"861","email":"webcastpoa6#myhost","name":"name_6"},{"id":"862","email":"webcastpoa7#myhost","name":"name_7"},{"id":"863","email":"webcastpoa8#myhost","name":"name_8"},{"id":"864","email":"webcastpoa9#myhost","name":"name_9"},{"id":"865","email":"webcastpoa10#myhost","name":"name_10"},{"id":"866","email":"webcastpoa11#myhost","name":"name_11"},{"id":"867","email":"webcastpoa12#myhost","name":"name_12"},{"id":"868","email":"webcastpoa13#myhost","name":"name_13"},{"id":"869","email":"webcastpoa14#myhost","name":"name_14"},{"id":"870","email":"webcastpoa15#myhost","name":"name_15"},{"id":"871","email":"webcastpoa16#myhost","name":"name_16"},{"id":"872","email":"webcastpoa17#myhost","name":"name_17"},{"id":"873","email":"webcastpoa18#myhost","name":"name_18"},{"id":"874","email":"webcastpoa19#myhost","name":"name_19"},{"id":"875","email":"webcastpoa20#myhost","name":"name_20"},{"id":"876","email":"webcastpoa21#myhost","name":"name_21"},{"id":"877","email":"webcastpoa22#myhost","name":"name_22"},{"id":"878","email":"webcastpoa23#myhost","name":"name_23"},{"id":"879","email":"webcastpoa24#myhost","name":"name_24"},{"id":"880","email":"webcastpoa25#myhost","name":"name_25"},{"id":"881","email":"webcastpoa26#myhost","name":"name_26"},{"id":"882","email":"webcastpoa27#myhost","name":"name_27"},{"id":"883","email":"webcastpoa28#myhost","name":"name_28"},{"id":"884","email":"webcastpoa29#myhost","name":"name_29"},{"id":"885","email":"webcastpoa30#myhost","name":"name_30"},{"id":"886","email":"webcastpoa31#myhost","name":"name_31"},{"id":"887","email":"webcastpoa32#myhost","name":"name_32"},{"id":"888","email":"webcastpoa33#myhost","name":"name_33"},{"id":"889","email":"webcastpoa34#myhost","name":"name_34"},{"id":"890","email":"webcastpoa35#myhost","name":"name_35"},{"id":"891","email":"webcastpoa36#myhost","name":"name_36"},{"id":"892","email":"webcastpoa37#myhost","name":"name_37"},{"id":"893","email":"webcastpoa38#myhost","name":"name_38"},{"id":"894","email":"webcastpoa39#myhost","name":"name_39"},{"id":"895","email":"webcastpoa40#myhost","name":"name_40"},{"id":"896","email":"webcastpoa41#myhost","name":"name_41"},{"id":"897","email":"webcastpoa42#myhost","name":"name_42"},{"id":"898","email":"webcastpoa43#myhost","name":"name_43"},{"id":"899","email":"webcastpoa44#myhost","name":"name_44"}
]}
I have the following code so far.
using System.Windows.Forms;
using MySql.Data.MySqlClient;
using System.Net;
using System.IO;
using Newtonsoft.Json;
WebClient client = new WebClient();
string reply = client.DownloadString("http://192.168.1.115/php_poa/test_select.php");
MessageBox.Show(reply);
records p1 = JsonConvert.DeserializeObject<records>(reply);
MessageBox.Show(p1.ToString());
class records
{
public string id { get; set; }
public string email { get; set; }
public string name { get; set; }
}
And would like a for loop to process each record.
THanks
Just use List<record> instead. You won't require to iterate it through loop.
Example:
List<records> p1 = JsonConvert.DeserializeObject<List<records>>(reply);
Remember to add following namespace at top
using System.Collection.Generic;
You need to use next class for deserialization:
class MyResponse
{
public List<Records> Records { get; set; }
}
class Records
{
public string Id { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
And now you can deserialize your web response:
MyResponse myResponse = JsonConvert.DeserializeObject<MyResponse>(reply);
to iterate through a loop you can do something like this
JObject records = JObject.Parse(json);
foreach (var record in obj["records"])
{
records p1 = JsonConvert.DeserializeObject<records>(reply);
}
although i would suggest creating a constructor for your class instead of using DeserializeObject
like this
public Records(JToken toekn)
{
Id = (string)toekn["Id "];
Email = (string)toekn["Email "];
Name = (string)toekn["Name "];
}
and use
Records record = new Records(record) instead of DeserializeObject inside the loop
This loads a set of values from an XML file and places them into a class for storage. I'm trying to figure out how to output the values as a list so I can place them into a Listbox.
I thought there would be an easy way like a .ToList() method or to be able to foreach through the strings in the class (no public GetEnumerator). I've been able to find out that Foreach hides some of the complexity but not away to do what I want.
I've been searching online with no avail (lacking the correct terminology maybe), unfortunately I left my C# reference books at work :/
Would much appreciate a pointer in the right direction,
Thanks.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
namespace ThereIsOnlyRules
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
listBox1.Items.Clear();
string path = "characterXML.xml";
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
System.Xml.XmlDocument CXML = new System.Xml.XmlDocument();
CXML.Load(fs);
//Get the number of elements
XmlNodeList elemList = CXML.GetElementsByTagName("unit");
//foreach (var element in elemList)
//{
// listBox1.Items.Add(element);
//}
for (int i = 0; i < elemList.Count; i++)
{
UnitAttributes attributes = new UnitAttributes();
attributes.army = elemList[i].Attributes["army"].Value;
attributes.category = elemList[i].Attributes["category"].Value;
attributes.type = elemList[i].Attributes["type"].Value;
attributes.composition = elemList[i].Attributes["composition"].Value;
attributes.WS = elemList[i].Attributes["WS"].Value;
attributes.BS = elemList[i].Attributes["BS"].Value;
attributes.T = elemList[i].Attributes["T"].Value;
attributes.W = elemList[i].Attributes["W"].Value;
attributes.I = elemList[i].Attributes["I"].Value;
attributes.A = elemList[i].Attributes["A"].Value;
attributes.LD = elemList[i].Attributes["LD"].Value;
attributes.save = elemList[i].Attributes["Save"].Value;
attributes.armour = elemList[i].Attributes["armour"].Value;
attributes.weapons = elemList[i].Attributes["weapons"].Value;
attributes.specialrules = elemList[i].Attributes["specialrules"].Value;
attributes.transport = elemList[i].Attributes["transport"].Value;
attributes.options = elemList[i].Attributes["options"].Value;
//foreach (string item in attributes)
//{
//unit.Add(item);
//}
//listBox1.Items.AddRange(attributes)
}
//Close the filestream
fs.Close();
}
catch (Exception ex)
{
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ThereIsOnlyRules
{
class UnitAttributes
{
public string army { get; set; }
public string category { get; set; }
public string type { get; set; }
public string composition { get; set; }
public string WS { get; set; }
public string BS { get; set; }
public string T { get; set; }
public string W { get; set; }
public string I { get; set; }
public string A { get; set; }
public string LD { get; set; }
public string save { get; set; }
public string armour { get; set; }
public string weapons { get; set; }
public string specialrules { get; set; }
public string transport { get; set; }
public string options { get; set; }
}
}
<?xml version="1.0"?>
<config>
<unit
army="Tyranids"
category="Troops"
type="Infantry"
composition="10-30"
WS="3"
BS="3"
T="3"
W="1"
I="4"
A="1"
LD="6"
Save="6+"
armour="Chitin"
weapons="Claws and Teeth, Fleshborer"
specialrules="Instictive Behaviour - Lurk, Move Through Cover"
transport="If the brood consists of 20 models or less, it may take a Mycetic Spore."
options="Strangleweb, Spinefists, Spike rifle, Devourer, Adrenal Glands, Toxin Sacs"
>
Termagant Brood
</unit>
<unit
army="Tyranids"
category="Troops"
type="Infantry"
composition="10-30"
WS="3"
BS="3"
T="3"
W="1"
I="5"
A="2"
LD="6"
Save="6+"
armour="Chitin"
weapons="Scything Talons"
specialrules="Instictive Behaviour - Feed, Bounding Leap, Fleet, Move Through Cover"
transport="If the brood consists of 20 models or less, it may take a Mycetic Spore."
options="Adrenal Glands, Toxin Sacs"
>
Hormagaunt Brood
</unit>
</config>
Are the members of your class fields or properties? Either way, a little reflection and Linq should allow you to enumerate through the data members of your class, after you have hydrated an instance of it from your XML file.
var fieldDictionary =
(from f in typeof(UnitAttributes).GetFields()
select new {Name = f.Name, Value = (string)(f.GetValue(attributes))})
.ToDictionary(x=>x.Name, x=>x.Value);
fieldDictionary is now a Dictionary<string, string> (which is an IEnumerable<KeyValuePair<string, string>>), which should be suitable for loading into a ListBox.
Be advised; reflection is slow. It would be far more preferable for you to modify or extend your UnitAttributes class to implement IEnumerable (probably of a Tuple, maybe a KeyValuePair). It would also allow you to enumerate the properties of an instance of the class in exactly the order you want, instead of the order in which they're defined, or by some other FieldInfo/PropertyInfo data like the field's name.
Also be aware that a field is not a property, and vice versa. If you have a mix of properties and public fields on your class, I would HIGHLY recommend standardizing to one or the other; otherwise you'll have to reflect BOTH a list of properties and a list of fields using two of the above Linq statements (at a cost of roughly double the run time), and there will be no chance of them being in any custom-defined order.
You'll save yourself a lot of time and effort if you use a common serializer like XmlSerializer to handle converting your objects to/from strings. You don't have to write this type of code from scratch.