How to save a human readable file - c#

Currently i have an application that reads and writes several properties from one or two basic classes to a .txt file using the Binary Serializer.
I've opened up the .txt file in NotePad and as it's formatted for the application it's not very readable to the human eye, not for me anyway =D
I've heard of using XML but pretty much most of my searches seem to overcomplicate things.
The kind of data im trying to save is simply a collection of "Person.cs" classes,nothing more than a name and address, all private strings but with properties and marked as Serializable.
What would be the best way to actually save my data in a way that can be easily read by a person? It would also make it easier to make small changes to the application's data directly in the file instead of having to load it, change it and save it.
Edit:
I have added the current way i am saving and loading my data, my _userCollection is as it suggests and the nUser/nMember are an integer.
#region I/O Operations
public bool SaveData()
{
try
{
//Open the stream using the Data.txt file
using (Stream stream = File.Open("Data.txt", FileMode.Create))
{
//Create a new formatter
BinaryFormatter bin = new BinaryFormatter();
//Copy data in collection to the file specified earlier
bin.Serialize(stream, _userCollection);
bin.Serialize(stream, nMember);
bin.Serialize(stream, nUser);
//Close stream to release any resources used
stream.Close();
}
return true;
}
catch (IOException ex)
{
throw new ArgumentException(ex.ToString());
}
}
public bool LoadData()
{
//Check if file exsists, otherwise skip
if (File.Exists("Data.txt"))
{
try
{
using (Stream stream = File.Open("Data.txt", FileMode.Open))
{
BinaryFormatter bin = new BinaryFormatter();
//Copy data back into collection fields
_userCollection = (List<User>)bin.Deserialize(stream);
nMember = (int)bin.Deserialize(stream);
nUser = (int)bin.Deserialize(stream);
stream.Close();
//Sort data to ensure it is ordered correctly after being loaded
_userCollection.Sort();
return true;
}
}
catch (IOException ex)
{
throw new ArgumentException(ex.ToString());
}
}
else
{
//Console.WriteLine present for testing purposes
Console.WriteLine("\nLoad failed, Data.txt not found");
return false;
}
}

Replace your BinaryFormatter with XMLSerializer and run the same exact code.
The only change you need to make is the BinaryFormatter takes an empty constructor, while for the XMLSerializer you need to declare the type in the constructor:
XmlSerializer serializer = new XmlSerializer(typeof(Person));

Using XmlSerializer is not really complicated. Have a look at this MSDN page for an example: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

You could implement your own PersonsWriter, that takes a StreamWriter as constructor argument and has a Write method that takes an IList<Person> as input to parse out a nice text representation.
For example:
public class PersonsWriter : IDisposable
{
private StreamWriter _wr;
public PersonsWriter(IList<Person> persons, StreamWriter writer)
{
this._wr = writer;
}
public void Write(IList<Persons> people) {
foreach(Person dude in people)
{
_wr.Write(#"{0} {1}\n{2}\n{3} {4}\n\n",
dude.FirstName,
dude.LastName,
dude.StreetAddress,
dude.ZipCode,
dude.City);
}
}
public void Dispose()
{
_wr.Flush();
_wr.Dispose();
}
}

YAML is another option for human readable markup that is also easy to parse. there are libraries available for c# as well as almost all other popular languages. Here's a sample of what yaml looks like:
invoice: 34843
date : 2001-01-23
bill-to: &id001
given : Chris
family : Dumars
address:
lines: |
458 Walkman Dr.
Suite #292
city : Royal Oak
state : MI
postal : 48046

Frankly, as a human, I don't find XML to be all that readable. In fact, it's not really designed to be read by humans.
If you want a human readable format, then you have to build it.
Say, you have a Person class that has a First Name, a last Name and a SSN as properties. Create your file, and have it write out 3 lines, with a description of the field in the first fifty (random number from my head) and then with character 51 have the value start being written.
This will produce a file that looks like:
First Name-------Stephen
Last Name -------Wrighton
SSN -------------XXX-XX-XXXX
Then, reading it back in, your program would know where the data begins on each line, and what each line is for (the program would know that Line 3 is the SSN value).
But remember, to truly gain human readability, you sacrifice data portability.

Try the DataContractSerializer
It serializes objects to XML and is very easy to use

Write a CSV reader writer if you want a good compromise between human and machine readable in a Windows environment
Loads into Excel too.
There's a discussion about it here:
http://knab.ws/blog/index.php?/archives/3-CSV-file-parser-and-writer-in-C-Part-1.html
EDIT
That is a C# article... it just confusingly has "C" in the URL.

I really think you should go with XML (look into DataContractSerializer). Its not that complicated. You could probably even just replace BinarySerializer with XMLSerializer and go.
If you still don't want to do that, though, you can write a delimited text file. Then you'll have to write your own reader method (although, it could almost just use the split method).
//Inside the Person class:
public override string ToString()
{
List<String> propValues = new List<String>();
// Get the type.
Type t = this.GetType();
// Cycle through the properties.
foreach (PropertyInfo p in t.GetProperties())
{
propValues.add("{0}:={1}", p.Name, p.GetValue(o, null));
}
return String.Join(",". propValues.ToArray())
}
using (System.IO.TextWriter tw = new System.IO.StreamWriter("output.txt"))
{
tw.WriteLine(person.ToString());
}

Related

How to save a list of classes in combination with other variables in c# wpf

Hi I just started working with C# WPF and I read about Serialization to store or load your data. My Question is how can I store a class that contains a list of another class and some additional parameters?
My first class (MeterValues) contains a number of parameters (type,speed,etc..)
public class MeterValues{}
I now made a second class to store a list containing multiple instances of the first class type. (So if I have 3 different meters, this list size = 3)
public class MeterValuesList : IList<MeterValues>{}
Now I wish to add an additional parameter to the second class, something independent of the first class so it should only be saved once. (not for every instance of class1)
To make my problem clear, I could add the extra parameter to the first class, but then If I have 100 different meters, the parameter is stored 100 times, and I only need to store it once.
Any idea on how to do this?
PS: If you need any additional information please just ask, I'm very eager to learn and to assist you in helping me solve this problem. Thanks in advance.
UPDATE:
I'm able to save the class MeterValuesList to a .xml file but only the List gets stored in the file, the extra parameter does not show up (It is in the class right before I write it to the file, checked it with debugger but does not show up in the file)
MeterValuesList meterValuesList = DataContext as MeterValuesList;
meterValuesList.CommSettings = "Com5:19200,8,n,1";
FileStream stream = null;
try
{
stream = new FileStream(filename, FileMode.Create, FileAccess.Write);
XmlSerializer serializer = new XmlSerializer(typeof(MeterValuesList));
serializer.Serialize(stream, meterValuesList);
stream.Close();
}
This is the result after saving the class to an xml file. The extra parameter is missing.
<?xml version="1.0"?>
<ArrayOfMeterValues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MeterValues></MeterValues>
</ArrayOfMeterValues>
Unless you plan on overriding or expanding the functionality that an IList has in place, there is no reason to inherit from it in your MeterValuesList class. This feels like a case of using a "has a" relationship instead of an "is a" relationship.
Try this instead:
public class MeterValuesGroup
{
List<MeterValues> MeterList { get; set; }
int ExtraParameter { get; set; }
// whatever additional parameters you need here.
}
If you do need to inherit from IList or IEnumerable, you can do something similar. However, in order to serialize this class correctly, you'll have to implement IXmlSerializable in MeterValues and MeterValuesList.
(Here is an excellent example of how this will look: Proper way to implement IXmlSerializable?)
public class MeterValuesList : IList<MeterValues>, IXmlSerializable
{
MeterValues[] _MeterList { get; set; }
string CommSettings = "Com5:19200,8,n,1";
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("CommSettings ", CommSettings );
foreach (var mv in _MeterList)
{
// kind of a bad example, but hopefully you get the idea
if (mv== null)
return;
writer.WriteStartElement("MeterValues");
mv.WriteXml(writer);
writer.WriteEndElement();
}
}
You can add this property in the second class MeterValueList and serilize this class and when you deserialize it , that additional property will be assigned.
MeterValueList m = new MeterValueList();
m.AdditionalParameter = 100;
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, m);
byte[] yourBytesToDb = memorystream.ToArray();
//here you write yourBytesToDb to database
//----------read from database---------------------
//here you read from database binary data into yourBytesFromDb
MemoryStream memorystreamd = new MemoryStream(yourBytesFromDb);
BinaryFormatter bfd = new BinaryFormatter();
MeterValueList md = bfd.Deserialize(memorystreamd) as MeterValueList ;
var i = md.AdditinalParameter; // must print 100

xml xsd validation and malformed xml

I wrote a quick class to validate an XML file at a FilePath against an XSD with .NET (see below).
I have large volumes of data files being generated by another machine on the LAN, but the files are not true XML, they are malformed, but in the same way every time and based on their structure I can make some global replaces on the content of the file to correct it. So I have to correct these before testing with XSD. I have to replace <\ with </ and so on. All the replaces are listed in the code.
When I point this to the LAN network share of the machine generating the files at a list of about 50k files, and this took about 15 minutes to complete. I'm wondering if this is just IO capped by the LAN, or if there's a better (quicker) way to correct the malformed XML than the replaces I do here.
class VCheck
{
private static XmlReaderSettings settings = new XmlReaderSettings();
private bool valid;
string message;
public string Message { get { return message; } }
public VCheck()
{
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
settings.Schemas.Add(null, "schema.xsd");
}
public bool CheckFile(string FileFullPath)
{
StreamReader file = new StreamReader(FileFullPath);
valid = true;
message = null;
try
{ //setup xml reader with settings
XmlReader xml = XmlReader.Create(new StringReader(#"<?xml version='1.0'?><root xmlns=""MYE"">" +
file.ReadToEnd().Replace(#"<\", #"</").Replace("&", "&").Replace("\"", """).Replace("'", "&apos;") + "</root>"),
settings);
while (xml.Read()) ; //read in all xml, validating against xsd
}
catch
{
//problem reading the xml file in, bad path, disk error etc.
return false;
}
return valid;
}
void ValidationCallBack(object sender, ValidationEventArgs e) //called on failed validations
{
valid = false;
message = e.Message;
switch (e.Severity)
{
case XmlSeverityType.Error:
//Do stuff on validation error
break;
case XmlSeverityType.Warning:
//Do stuff on validation warning
break;
}
}
}
I'd call it from main like this:
static void Main(string[] args)
{
VCheck checker = new VCheck();
foreach (string file in files) //files is a List<string> of file paths/names
{
if (!checker.CheckFile(file))
{
//To do stuff if not valid
}
}
}
I don't think reading it all into memory - ReadToEnd - and performing String.Replace on the contents is a good choice, with regard to your performance concerns.
If I were you, I'd rather rewrite those files "piece by piece" - that is, buffering and replacing data on the fly.
Just create a new file, load some of the malformed file into the buffer (say 4 kb), do the replacements, flush the results into your newly created file; rinse and repeat.
Beware: it can happen that one buffer ends with < and next one starts with \. If you want not to miss any <\s (and the like), you need to handle such cases as well.
Another possible solution is that you could try and create your own implementation of a "more tolerant" XmlReader (this class is not sealed, so you can base on it and create your own), although personally I haven't done it and I'm not sure this would be a good approach. Rewriting the files will at least leave you with syntactically valid XML, which may come in useful at some point.
PS. On a side note:
catch
{
//problem reading the xml file in, bad path, disk error etc.
return false;
}
I wouldn't do that. It leaves the caller with no idea whatsoever as for why the operation failed.
The quickest processes are those which do not need to be performed at all. So I commend Michael Kay's comments on dealing with "non-well-formed XML" to your attention.
If the non-XML data you'd like to handle as XML is being generated by a machine, there's no reason that that machine could not be generating XML data instead of the non-XML data you're currently trying to fix. Worse, every minute of effort you put into dealing with the errors in the data-producing process is a minute you've put into persuading those responsible for that process that they are producing correct, well-formed XML. So it's not only yourself you're hurting here.

Issues using StreamReader.EndOfStream?

So I'm doing a project where I am reading in a config file. The config file is just a list of string like "D 1 1", "C 2 2", etc. Now I haven't ever done a read/write in C# so I looked it up online expecting to find some sort of rendition of C/C++ .eof(). I couldn't find one.
So what I have is...
TextReader tr = new StreamReader("/mypath");
Of all the examples online of how I found to read to the end of a file the two examples that kept occurring were
while ((line = tr.ReadLine() != null)
or
while (tr.Peek() >= 0)
I noticed that StreamReader has a bool EndOfStream but no one was suggesting it which led me to believe something was wrong with that solution. I ended up trying it like this...
while (!(tr as StreamReader).EndOfStream)
and it seems to work just fine.
So I guess my question is would I experience issues with casting a TextReader as a StreamReader and checking EndOfStream?
One obvious downside is that it makes your code StreamReader specific. Given that you can easily write the code using just TextReader, why not do so? That way if you need to use a StringReader (or something similar) for unit tests etc, there won't be any difficulties.
Personally I always use the "read a line until it's null" approach - sometimes via an extension method so that I can use
foreach (string line in reader.EnumerateLines())
{
}
EnumerateLines would then be an extension method on TextReader using an iterator block. (This means you can also use it for LINQ etc easily.)
Or you could use ReadAllLines, to simplify your code:
http://msdn.microsoft.com/en-us/library/s2tte0y1.aspx
This way, you let .NET take care of all the EOF/EOL management, and you focus on your content.
No you wont experience any issue's. If you look at the implementation if EndToStream, you'll find that it just checks if there is still data in the buffer and if not, if it can read more data from the underlying stream:
public bool EndOfStream
{
get
{
if (this.stream == null)
{
__Error.ReaderClosed();
}
if (this.charPos < this.charLen)
{
return false;
}
int num = this.ReadBuffer();
return num == 0;
}
}
Ofcourse casting in your code like that makes it dependend on StreamReader being the actual type of your reader which isn't pretty to begin with.
Maybe read it all into a string and then parse it: StreamReader.ReadToEnd()
using (StreamReader sr = new StreamReader(path))
{
//This allows you to do one Read operation.
string contents = sr.ReadToEnd());
}
Well, StreamReader is a specialisation of TextReader, in the sense that StreamReader inherits from TextReader. So there shouldn't be a problem. :)
var arpStream = ExecuteCommandLine(cmd, arg);
arpStream.ReadLine(); // Read entries
while (!arpStream.EndOfStream)
{
var line1 = arpStream.ReadLine().Trim();
// TeststandInt.SendLogPrint(line, true);
}

Deserialization problem in .NET

I created a binary serialized file of my object, so that I could use it as a database file for many other projects. It works quite fine with the same project, but when I try to deserialize that file from other projects, it won't. The error that appears says, "xxxx.xxxx assembly not found". So, how should I serialize the object in order to make it assembly independent???
Here's my code :
// Binary formatter
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("BinarySerialization.bin",
FileMode.Create,
FileAccess.Write, FileShare.None);
formatter.Serialize(stream, words);
stream.Close();
What amendments do I need to do??? Please provide me with a working code example/sample.
Thanks.
BinaryFormatter is a type-based serialized; it deeply embeds type metadata into the output. I would wager that you have copied the class definition between the projects - that isn't enough for BinaryFormatter, since that is no longer the same Type (types are bound to their assembly).
In your scenario, it sounds to me that the correct thing to do here would be to use a contract-based serializer; for example:
xml (XmlSerializer, DataContractSerializer, etc)
json (JavascriptSerializer, JSON.net, etc)
binary (protobuf-net, etc)
There would would entirely in your scenario, and would also have much better version tolerance (BinaryFormatter is very brittle with versions)
You mention "XMLs aren't safe here, as I don't want the users to know the contents of the main database file." - in that case protobuf-net has the "advantage" of being not human-readable, but note: none of these, nor BinaryFormatter are encrypted; if I wanted to, I could obtain the contents if I really, really wanted to. If you need strong security, use proper encryption. In which case your code becomes (in addition to maybe a few marker attributes):
using(var stream = new FileStream("BinarySerialization.bin", FileMode.Create,
FileAccess.Write, FileShare.None))
{
Serializer.Serialize(stream, words);
}
Edit to show (per comments) how to serialize Dictionary<string, List<Word>> where Word is a class with 2 string members (most of the code here is just plumbing to show a complete example):
using System;
using System.Collections.Generic;
using System.IO;
using ProtoBuf;
[ProtoContract]
public class Word {
[ProtoMember(1)]
public string Foo { get; set; }
[ProtoMember(2)]
public string Bar { get; set; }
}
static class Program {
public static void Main() {
var data = new Dictionary<string, List<Word>>{
{"abc", new List<Word> {
new Word { Foo = "def", Bar = "ghi"},
new Word { Foo = "jkl", Bar = "mno"}
}},
{"pqr", new List<Word> {
new Word {Foo = "stu", Bar = "vwx"}
}}
};
using(var file = File.Create("my.bin")) {
Serializer.Serialize(file, data);
}
Dictionary<string, List<Word>> clone;
using(var file = File.OpenRead("my.bin")) {
clone = Serializer.Deserialize<
Dictionary<string, List<Word>>>(file);
}
foreach(var pair in clone) {
Console.WriteLine(pair.Key);
foreach(var word in pair.Value){
Console.WriteLine("\t{0} | {1}", word.Foo, word.Bar);
}
}
}
}
I would put all my models that have to be serialized into a seperate assembly.
Then you reference this assembly everywhere you need to deserialize the Models.
If not you need some kind of generator, that recreates the models based on some schema (same as WCF does with its utilities) or use plain formats like XML to persist your data.
The serialization mechanism in .NET creates a helper dll out of the type to serialize and deserialize your data in runtime. First it will spit out a code file that gets compiled and then the helper dll is loaded to do the serialization and deserialization.
If for some reason something happens while the helper .dll gets created - let's say a compilation error- then the runtime will not found this dll.
If the dll name in your case is some random character then I would say you are facing the problem described above. You can troubleshoot this by switching on an undocument switch. See the following article:
HOW TO: Debug into a .NET XmlSerializer Generated Assembly

Which way to save custom types to a file on a mobile device?

I have a list of custom types, they need to be saved/loaded to/from a file on a mobile device (Windows Mobile 6) what method would be the most suited, taking into account the limited resources of the device?
EDIT:
The data file will be around 2-5mb
How much data are we talking about?
I used an old device (HTC-s620, with a TI OMAP 850 200 MHz processor) to save a 2mb XML file using XML Serialization, in a matter of 3-5 seconds. Very simple programming model. Very easy to do. With a newer device I'm sure it would be much faster.
My usage scenario was, one full load, and one full save, per run.
[XmlRoot("notes")]
public class NoteList : List<Note>
{
// Set this to 'default' or 'preserve'.
[XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]
public string space = "preserve";
public static void Save(NoteList noteList, string NotesFilePath)
{
if (noteList == null) return;
XmlSerializer serializer = new XmlSerializer(typeof(NoteList));
string tmpPath = NotesFilePath + ".tmp";
using (System.IO.FileStream fs = new FileStream(tmpPath, FileMode.Create, FileAccess.Write))
{
serializer.Serialize(fs, noteList);
fs.Close();
}
if (File.Exists(tmpPath))
{
if (File.Exists(NotesFilePath))
{
string oldFile = NotesFilePath + ".bak";
if (File.Exists(oldFile)) File.Delete(oldFile);
File.Move(NotesFilePath, oldFile);
}
File.Move(tmpPath, NotesFilePath);
}
}
public static NoteList Load(string NotesFilePath)
{
if (!System.IO.File.Exists(NotesFilePath))
return null;
NoteList noteList = new NoteList();
XmlSerializer serializer = new XmlSerializer(noteList.GetType());
using (FileStream fs = new FileStream(NotesFilePath, FileMode.Open, FileAccess.Read))
{
noteList = (NoteList)serializer.Deserialize(fs);
fs.Close();
}
return noteList;
}
}
Devices capable of running Win Mobile 6, seem to be able to handle themselves quite well using the "crippled" .NET framework.
But if you want something faster then what System.IO provides, I think you're out of luck (?).
With "custom types", I gather you're referring to classes, and by that I suspect some .NET compliant classes? Then I don't see how you're going to squeeze any more juice...at least no more than
this happy camper got.
I'd recommend looking at SqlLite if you want a proper db but without the bloat. SqlLite is also atomic and power resilient. Otherwise saving to a flat file isn't a problem. Just remember that the power can die on you at any point, including half-way through your write.

Categories