XML to List converter? Is XML the right tool anyway? - c#

i'm coming from PHP to C#, so please excuse some of my terminology.
Currently i'm working on a small project that requires multiple profiles to be stored in one single file, so i decided to use XML, because INI files (usually my go to guy for text based stuff) are not really supported by C#. At least not in a satisfying way.
Here the basic structure of my XML file:
<profile name="FooBar">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
<profile name="XYZ">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
In PHP i would generate an associative array with the following structure:
<?php
foo = array(
'FooBar' => array(
'btnA_pressed' => 'actionA',
'btnA_released' => 'actionB'
// ...
),
'XYZ' => array(
// ...
)
);
EDIT START
Application Class Structure:
Settings (Contains all profiles and a reference to the current profile)
Profile (See below)
The Profile class:
public class Profile
{
private string _name;
public string Name
{
get { return this._name; }
set { this._name = value;}
}
private string _btnA_pressed;
public string BtnA_Pressed { get; set; }
// and so on...
public Profile(var data) { // arg is a placeholder
// associate the data with the properties
}
}
In short, the Settings class holds all profiles and a reference to the selected profile. Access to the profile goes over Settings.CurrentProfile.propertie_Name()
EDIT END
The question is now, how do i achieve the same or a similar thing in C#? Or are there better methods of achieving the same thing?
Thanks for your help in advance!

XML structures can be manipulated very easily with LINQ2XML without the need of typed models (classes).
Reading XML file containing many profile nodes (and i assume your XML file is correct and has one root node), can look like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
foreach (var profile in profiles)
{
Console.WriteLine(profile.Attribute("name").Value);
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
}
The output is:
FooBar
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
XYZ
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
Reading a single profile XML structure from a string and then creating a new one can look like this:
var xmlCode = #"<profile name=""FooBar""><btnA pressed=""actionA"" released=""actionB"" /><btnB pressed=""actionX"" released=""actionY"" /></profile>";
try
{
// read existing XML structure
var xml = XDocument.Parse(xmlCode); // XDocument.Load("c:\\path\\to\\file.xml");
// find all button elements
var buttons = xml.Root
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
// create xml
// root element
var newXml = new XElement("profile", new XAttribute("name", "FooBaz"),
new XElement("btnA",
new XAttribute("pressed", "actionX"),
new XAttribute("released", "actionXR")),
new XElement("btnB",
new XAttribute("pressed", "actionZ"),
new XAttribute("released", "actionZR")));
Console.WriteLine(newXml.ToString());
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
The output is:
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
<profile name="FooBaz">
<btnA pressed="actionX" released="actionXR" />
<btnB pressed="actionZ" released="actionZR" />
</profile>
You can use LINQ2XML to read the data and fill a list of objects of your Profile type like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
var listOfProfiles = new List<Profile>();
foreach (var profile in profiles)
{
var profileObject = new Profile("");
profileObject.Name = profile.Attribute("name").Value;
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
profileObject.BtnA_Pressed = pressed;
}
listOfProfiles.Add(profileObject);
}
You can also use XML serialization - you need to describe your XML structure as a class (typed model) and deserialize (read XML file into your class) resp. serialize (write your XML structure to a file). A generic implementation of the methods for serialization resp. deserialization can look like this:
public void SerializeModel<T>(string fqfn, T entity)
{
var xmls = new XmlSerializer(entity.GetType());
var writer = new StreamWriter(fqfn);
xmls.Serialize(writer, entity);
writer.Close();
}
public T DeserializeModel<T>(string fqfn)
{
var fs = new FileStream(fqfn, FileMode.Open);
var xmls = new XmlSerializer(typeof(T));
var r = (T) xmls.Deserialize(fs);
fs.Close();
return r;
}
The typed model that describes your Profile class and the lists contained within, looks like this (note the usage of the different XML serialization attributes):
public class Profiles
{
[XmlElement(ElementName="Profile")]
public List<Profile> Profile { get; set; }
}
public class Profile
{
[XmlArray(ElementName="Buttons")]
public List<Button> Buttons { get; set; }
[XmlAttribute]
public String Name;
}
public class Button
{
[XmlAttribute]
public String Pressed { get; set; }
[XmlAttribute]
public String Released;
}
Creation of an XML file:
var profiles = new Profiles();
var profileA = new Profile();
var profileB = new Profile();
var buttonA = new Button();
var buttonB = new Button();
profileA.Buttons = new List<Button>();
profileB.Buttons = new List<Button>();
profiles.Profile = new List<Profile>();
profileA.Name = "Profile A";
profileB.Name = "Profile B";
buttonA.Pressed = "Pressed A";
buttonA.Released = "Release A";
buttonB.Pressed = "Pressed B";
buttonB.Released = "Release B";
profileA.Buttons.Add(buttonA);
profileB.Buttons.Add(buttonB);
profiles.Profile.Add(profileA);
profiles.Profile.Add(profileB);
var xmlFile = "d:\\temp\\xml\\profile_model.xml";
SerializeModel<Profiles>(xmlFile, profiles);
The new XML file looks like this (note, the structure was slightly modified because of the way XML serialization in .NET handles arrays/lists):
<?xml version="1.0" encoding="utf-8"?>
<Profiles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Profile Name="Profile A">
<Buttons>
<Button Released="Release A" Pressed="Pressed A" />
</Buttons>
</Profile>
<Profile Name="Profile B">
<Buttons>
<Button Released="Release B" Pressed="Pressed B" />
</Buttons>
</Profile>
</Profiles>
The file can be then read like this:
var input = DeserializeModel<Profiles>(xmlFile);
foreach (var profile in input.Profile)
{
var b = profile.Buttons.First();
Console.WriteLine(String.Format("{0} - {1} - {2}", profile.Name, b.Pressed, b.Released));
}
The output is as expected:
Profile A - Pressed A - Release A
Profile B - Pressed B - Release B
Both approaches have advantages and disadvantages.
IMO the answer to your question (changed a bit) Is XML the correct approach for saving structured data to a file? is - definitely yes! Nowadays XML is one of the standards for representing / manipulating / exchanging structured data and data generally (data kept in a string). As someone already mentioned INI files were not really meant to represent complex nested structures.

In contrast to PHP where you might be used to doing a lot of arrays and magic-string based stuff, C# is a Statically Typed language where it's usually recommended to create a proper, formally defined, structured Data Model which allows you to manipulate the Data you're dealing with in a strongly typed manner.
This means, for example, that if you're dealing with personal information related data, and you need to deal with the concepts of last name, first name, and age you will want to create a class containing these pieces of data in the form of Properties, like so:
public class Person
{
public string LastName {get;set;}
public string FirstName {get;set;}
public int Age {get;set;}
//And so on...
}
Notice how each property has an adequate Data Type that allows you to constrain what values it can contain. For example, the fact that the Age property is of type int (integer numbers) automatically means you can never have something like "abc" inside it, and code like this:
var person = new Person();
person.Age = "abc";
will also produce a compile-time error, rather than blowing up at run time, or producing any sorts of inconsistencies in stored data.
Likewise, if your Person objects (in the real world data you're trying to model) have a relation to, say an Address, you're also going to create the Address class:
public class Address
{
public string Line1 {get;set;}
public string Line2 {get;set;}
public string City {get;set;}
//And so on...
}
And then model this Person -> Address relationship by creating an additional property in the Person class:
public class Person
{
//.. All of the above.
public Address Address {get;set;}
}
Which can be illustrated with a diagram like this:
This approach has the following advantages:
It provides Compile Time checking for correctness of the code. Compile-Time errors can be trapped (and need to be corrected) very early in the development cycle. For example, you can't do something like:
person.LatsName where the property name LastName is mispelled LatsName because the compiler "knows" there is no such property in the object model and thus you recieve a compile-time error rather than having the application crash at run-time.
It provides IDE support for features such as AutoComplete because the IDE "knows" the object model you're dealing with and thus it knows what properties/methods/events (Members) every class has, and what are the expected parameters and data types for each of these.
So, to answer the overarching question, once you created a proper object model, you can use .Net's built-in Serialization/Deserialization mechanisms to transform instances of classes in your object model (with actual values and properties) to a data format that can be stored in a file on disk, such as XML.

Assuming you have a class for profile with all the relevant fields/properties, you can do
Profile profile = new Profile();
profile.actionA = "actionA"; // or whatever you have
var file = new System.IO.StreamWriter(#"c:\temp\Profile_as_xml.xml");
var writer = new System.Xml.Serialization.XmlSerializer(typeof(Profile));
writer.Serialize(file, profile);
file.Close();
See http://msdn.microsoft.com/en-us/library/ms172873.aspx

Related

CSV to Cascading Model using Entity Framework

I need to turn an automatic CSV file into multiple database columns using Entity Framework. It is set up so that each model has children. So that Animal contains a list of Types which contain a list of Classification. In this way Classification is a grandchild of Animal
Right now I have these three models that need to be filled by the CSV file. The file is formatted in the following way:
They are then pulled from the API into a Windows Desktop App as a cascading dropdown box. So far I've tried adding them to separate lists however that did not upload when using Entity Framework. The current way is to try to cascade down the list however I get an error
Sequence contains no events
Here is the portion of the code that I am having a problem with (had to edit due to work rules so classes are different):
var Animal = new List<AnimalModel>();
var lines = await ReadStreamAsync(new StreamReader(uploadModel.File.OpenReadStream()));
foreach(string l in lines)
{
Animal.Add(new AnimalModel
{
AnimalName = cells[0],
});
Animal.Last().Type.Add(new TypeModel
{
TypeName = cells[1],
});
Animal.Last().Type.Last().Classification.Add(new ClassificationModel
{
Type = Type.Last(),
ClassificationName = cells[2],
Color = cells[3],
Age = cells[4]
});
}
I resolved the issue. I needed to initialize the list within the code as I am not doing so within the model. The following worked:
var Animal = new List<AnimalModel>();
var lines = await ReadStreamAsync(new StreamReader(uploadModel.File.OpenReadStream()));
foreach(string l in lines)
{
Animal.Add(new AnimalModel
{
AnimalName = cells[0],
Type = new List<TypeModel>()
{
new TypeModel()
{
TypeName = cells[1]
}
}
});
And so on for the grandchild. I will have to clean this up as it is quite messy but this works for now.

Store new XML into existing Project Array with Constructor

I'll try to keep it short and thank you in advance.
I have created a Quiz. The Questions and answers, as well as the integer for the correct answer are done via get and set , into a constructor and then created in another class by just creating an object and giving it the parameters. it looks as follows:
allQuestions = new Question[3];
allQuestions[0] = new Question("Question1", "answer1", "answer2",
"answer3", "answer4", 2);
where 2 is the integer that says answer 2 is the correct one.
i do use this array in almost every function in my code.
Now i decided to get the questions from a XML Document instead of creating them here. I'm a C# beginner so i played around and could not get it working.
my self made xml looks as follows :
<questions>
<question>
<ID>1</ID>
<questiontext>Peter</questiontext>
<answer1>der</answer1>
<answer2>da</answer2>
<answer3>21</answer3>
<answer4>lol</answer4>
</question>
<question>
<ID>2</ID>
<questiontext>Paul</questiontext>
<antwort1>dasistid2</antwort1>
<antwort2>27</antwort2>
<antwort3>37</antwort3>
<antwort4>47</antwort4>
</question>
</questions>
so 2 basic nodes (?)
can you explain me how to read that one and store it into my array so i can still use my e.g. "allQuestions.Question1" ? watched a youtube tutorial, quite a lot, could still not get it working in this project.
using visual studio 2017 , WPF , C#
There are a lot of ways to do what you are trying to do. I'll give you a dirty example of a manual solution, as well as a more automatic one that should work. Note that the automatic version won't use your constructor so unless you have an empty constructor defined it may not work for you.
Manual processing using XML Linq:
public IList<Question> ParseXml(string xmlString)
{
var result = new List<Question>();
var xml = XElement.Parse(xmlString);
var questionNodes = xml.Elements("question");
//If there were no question nodes, return an empty collection
if (questionNodes == null)
return result;
foreach (var questionNode in questionNodes)
{
var idNode = questionNode.Element("ID");
var textNode = questionNode.Element("questiontext");
var ant1Node = questionNode.Element("antwort1");
var ant2Node = questionNode.Element("antwort2");
var ant3Node = questionNode.Element("antwort3");
var ant4Node = questionNode.Element("antwort4");
var question = new Question();
question.Id = Convert.ToInt32(idNode?.Value);
// note the '?.' syntax. This is a dirty way of avoiding NULL
// checks. If textNode is null, it returns null, otherwise it
// returns the textNode.Value property
question.Text = textNode?.Value;
question.AnswerOne = ant1Node?.Value;
question.AnswerTwo = ant2Node?.Value;
question.AnswerThree = ant3Node?.Value;
question.AnswerFour = ant4Node?.Value;
result.Add(question);
}
return result;
}
Next we have the XmlSerializer approach. This is not ideal in all situations, but provides an easy way to serialize objects into XML and to deserialize XML into objects.
public QuestionCollection autoparsexml(string xml)
{
//create the serializer
XmlSerializer serializer = new XmlSerializer(typeof(QuestionCollection));
//convert the string into a memory stream
MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
//deserialize the stream into a c# object
QuestionCollection resultingMessage = (QuestionCollection)serializer.Deserialize(memStream);
}
public class QuestionCollection
{
public List<Question> Questions { get; set; }
}

Couchbase Lite 2 + JsonConvert

The following code sample writes a simple object to a couchbase lite (version 2) database and reads all objects afterwards. This is what you can find in the official documentation here
This is quite a lot of manual typing since every property of every object must be transferred to the MutableObject.
class Program
{
static void Main(string[] args)
{
Couchbase.Lite.Support.NetDesktop.Activate();
const string DbName = "MyDb";
var db = new Database(DbName);
var item = new Item { Name = "test", Value = 5 };
// Serialization HERE
var doc = new MutableDocument();
doc.SetString("Name", item.Name);
doc.SetInt("Value", item.Value);
db.Save(doc);
using (var qry = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Database(db)))
{
foreach (var result in qry.Execute())
{
var resultItem = new Item
{
// Deserialization HERE
Name = result[DbName].Dictionary.GetString("Name"),
Value = result[DbName].Dictionary.GetInt("Value")
};
Console.WriteLine(resultItem.Name);
}
}
Console.ReadKey();
}
class Item
{
public string Name { get; set; }
public int Value { get; set; }
}
}
From my research Couchbase lite uses JsonConvert internally, so there might be a way to simplify all that with the help of JsonConvert.
Anything like:
var json = JsonConvert.SerializeObject(item);
var doc = new MutableDocument(json); // No overload to provide raw JSON
or maybe
var data = JsonConvert.SerializeToDict(item); // JsonConvert does not provide this
var doc = new MutableDocument(data);
Is there or is this some kind of optimization and the preferred approach is by intend?
People ask about this quite often, but Couchbase Lite does not actually store JSON strings in the database. They are stored in a different format so this would not give the benefit that you think (the JSON would need to be reparsed and then broken down into the other format). I'd been pushing for a way to serialize classes directly instead of going through dictionary objects (which seems like the ultimate goal here) but our priority is on things that enterprise clients want and this doesn't seem to be one of them. Note that for it to make it in, it needs to be implemented in C# Java and Objective-C / Swift.
I don't know about JsonConvert but there seems to be a constructor that takes IDictionary<string, object> as argument. So I would try something like this (brain-compiled):
MutableDocument CreateDocument(object data)
{
if (data == null) return null;
var propertyValues = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
propertyValues[property.Name] = property.GetValue(data);
}
return new MutableDocument(propertyValues);
}
See if this works.

Reading files from a txt file and placing contents in a List<T>

I have a file in my project folder containing the data for properties in a Vehicle class. If the class was hardcoded into the XAML it would look like this:
Vehicle v1 = new Car() { Make = "Ford", Model = "Fiesta", Price = 10000, Year = "1999", Colour = "Red", Mileage = 40000, Description = "Lovely red car, 4 wheel, optional steering wheel.", VehicleType = "Car" };
VehicleList.Add(v1);
I don't want to hardcode this class object in, Instead I want to read it from a .txt file, each part separated by a comma ',' and place these into each property and add that vehicle into the Vehicle List<> 'VehicleList' and then display this new read list into a listbox.
Both the hardcode and the .txt follow the same structure with the hardcode containing the variable names and the .txt file containing just the data.
Here is what I have so far, as you can see I tried using System.IO however I am open to alternative methods.
private void Button_Click(object sender, RoutedEventArgs e)
{
string location = #"PATH";
VehicleList = File.ReadAllLines(location).ToList();
var logFile = File.ReadAllLines(location);
foreach (var v[i] in logFile) VehicleList.Add(s);
}
}
public static List<Car> FromTextFile(string fileName)
{
var lines = File.ReadAllLines(fileName);
var cars = new List<Car>();
foreach (var line in lines)
{
var car = FromLine(line);
if (car == null)
continue;
cars.Add(car);
}
return cars;
}
private static Car FromLine(string line)
{
var values = line.Split(',');
if (values.Length != 8) // There is no specification on what to do, if the amount of items is not sufficient.
return null;
// There is also no specification on what order to use, or how to map the ordering.
return new Car
{
Make = values[0],
Model = values[1],
// There is also no specification on how to handle in-correct formats.
// This assumes the Price property to be type of int.
Price = int.Parse(values[2]),
Year = values[3],
Colour = values[4],
// Again; how to handle in-correct formats?
Mileage = int.Parse(values[5]),
Description = values[6],
VehicleType = values[7]
};
}
This code should do what you described. Please be aware of the limitations of this code;
It expects a file that contains some lines, in comma seperated format.
It expects lines to contain 8 non-empty items, otherwise those lines will not be mapped.
It assumes the comma-seperated items to be exactly in this order; Make, Model, Price, Year, Colour, Mileage, Description, VehicleType.
It will not handle in-correct formats for converted types (e.g int). Because you did not ask so.
Keep in mind that this is not a desired code, it is not well written.
Now, as to your code;
Firstly, you've two different values for lines. I don't know why. One of them should be ok.
Second, you try to loop through one of them using foreach, but it doesn't work like that. You don't give or take an index from the foreach loop. The correct what is just foreach (TYPE VARIABLE_NAME in COLLECTION). So your code would be foreach (var v /* not v[i] */ in logFile) // ...
And then you add this to a list which contains those seperated lines again. At the end of the day, you have a list that contains the same lines twice. What you should have done in the adding part is, you should have converted the line to a Car object. Now how you do that, is absolutely up to you. Here are some question that comes to my mind, when the task is something such as mapping a string collection to an object.
What do I do if some members do not exist? (i.e when there is 5 items in the line, even though we have 8 properties)
How do I know which property to map the i'th item? What is the ordering? Do I get it from a "Columns" line? Do I just assume some ordering of my desire? How do I handle, if the user does not follow those rules?
What do I do, when there is something wrong about formatting of some of the members?
Now, this is not really an un-common problem. Everyone somehow needs to transport the data from place A to place B. For your specific instance, it is from disk storage, to memory, in the form of an object.
How do people solve these sorts of problem? They invent generic ways of "serialization" and "deserialization". What are some examples? The most known ones and the most used ones that comes to my mind are XML (eXtensible Markup Language) and JSON (JavaScript Object Notation). What do these actually do? They serialize programming objects to non-binary string formats. Here is an example XML output for your Car class.
<?xml version=\"1.0\" encoding=\"utf-16\"?>
<Car xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Make>Ford</Make>
<Model>Fiesta</Model>
<Price>10000</Price>
<Year>1999</Year>
<Colour>Red</Colour>
<Mileage>40000</Mileage>
<Description>Lovely red car, 4 wheel, optional steering wheel.</Description>
<VehicleType>Car</VehicleType>
</Car>
Generated with this simple code;
var car = new Car
{
Make = "Ford",
Model = "Fiesta",
Price = 10000,
Year = "1999",
Colour = "Red",
Mileage = 40000,
Description = "Lovely red car, 4 wheel, optional steering wheel.",
VehicleType = "Car"
};
var serializer = new XmlSerializer(typeof(Car));
// https://stackoverflow.com/questions/2434534/serialize-an-object-to-string
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, car);
var output = textWriter.ToString();
}
And this is, how you would easily read "Car"s from a text file, if it was formatted correctly;
var serializer = new XmlSerializer(typeof(List<Car>));
// If your file should contain one car and one car only, you should change the above line to;
// var serilizer = new XmlSerializer(typeof(Car));
using (var reader = File.OpenRead(#"PATH"))
{
var cars = (List<Car>)serializer.Deserialize(reader);
reader.Close();
}
Similar code can be written for JSON. But I think you get the idea.
In short, most of the times, XML, JSON or a similar generic format is sufficient. When they are not, you can develop your very own format. But comma-seperated line is a very simplistic way of doing serialization. The reasons are exactly my question (see above). And XML, JSON answers those questions easily. Implementations do a very good job as well, most of the time.
Edit
Here is an answer to a very similar question. The answer to that question would high likely also answer your question. How to split csv whose columns may contain ,

Deserialize string of name=value format to object

I want to read files, each of which contains a person's details, as below, and convert it to a Person object.
Covert below
id=1
firstName=John
lastName=Smith
To:
public class Person
{
public int Id {get;set;}
public string FirstName{get;set;}
public string LastName{get;set;}
}
Are there .NET built-in methods to do that, or third party library. I cannot find it via google.
Update:
The file format CANNOT be changed.
.NET is really into XML, so you won't find build-in functionality for INI-like formats. But there are a bunch of libs that make it easy to read and write such files, e.g. ini-parser or nini, but you still have to do the mapping to and from objects manually.
You could parse the text with String.Split and LINQ:
Dictionary<string, string> dict = text
.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
.Select(e => e.Split('='))
.ToDictionary(strings => strings[0], strings => strings[1]);
Then use something like Dictionary Adapter.
For example using File.ReadAllLines, a little bit of Linq and String.Substring?
var lines = File.ReadAllLines(path).Select(l => l.Trim());
var idLine = lines.FirstOrDefault(l => l.StartsWith("id=", StringComparison.OrdinalIgnoreCase));
var lNameLine = lines.FirstOrDefault(l => l.StartsWith("lastname=", StringComparison.OrdinalIgnoreCase));
var fNameLine = lines.FirstOrDefault(l => l.StartsWith("firstname=", StringComparison.OrdinalIgnoreCase));
if (idLine != null && lNameLine != null && fNameLine != null)
{
Person person = new Person()
{
Id = int.Parse(idLine.Substring(idLine.IndexOf("=") + 1)),
FirstName = fNameLine.Substring(fNameLine.IndexOf("=") + 1),
LastName = lNameLine.Substring(lNameLine.IndexOf("=") + 1)
};
}
(assuming that there's just one person per file)
But i would use a different format like XML (or a database of course).
I really think you should consider changing your input data format into something more standard (like XML or JSON).
But that does not mean you can't read your file at all. You should just read your text file by your own:
var people = new List<Person>();
using (var stream = File.OpenRead("Input.txt"))
{
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
int id = int.Parse(reader.ReadLine().Substring(3));
string firstName = reader.ReadLine().Substring(10);
string lastName = reader.ReadLine().Substring(9);
var newPerson = new Person()
{
Id = id,
FirstName = firstName,
LastName = lastName
};
people.Add(newPerson);
}
}
}
If you have the data in a format like this:
<Person>
<Id>1</Id>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
</Person>
Then this C# code will desrialise into an instance of Person
//assuming you have a string called "data" that contains the above XML.
XDocument xd=XDocument.Parse(data); //needs System.Xml.Linq for XDocument type.
using(var reader = xd.CreateReader())
{
using(XmlSerializer ser = new XmlSerializer(typeof(Person))
{
Person p = ser.Deserialize(reader) as Person;
//p will be null if it didn't work, so make sure to check it!
}
}
Note that the deserializer is case sensitive so you need to make sure the element cases match the casing of the properties in your class (You can get arond this by decorating your properties with Serializer attributes that tell the serialzer how to map them here)
The plain native serialzer is great for simple objects like this but can trip you up on some data types like char, bool, etc, so do checkout that link on the attributes.
If you wanted to do it from the format you gave in the question, you'd need to write a custom serialiser, in your case my advice would be to read from your file and generate XML from the data using XDocument Hope that helps.

Categories