I'm attempting to deserialize a file and open it up in my program. In this particular program I can save / serialize my collection of customer objects to a file, but when I attempt to de-serialize and reopen the file in the program, it fails. Any Idea why this is failing?
The Following Code is my "Open" button click:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog sfd = new OpenFileDialog();
Nullable<bool> result = sfd.ShowDialog();
try
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (Stream fs = new StreamReader("data_to_write.xml"))
{
NewAccountList = bf.Deserialize(fs) as ObservableCollection<Contact>;
}
}
}
It has to be said that it is somewhat tricky to answer a question about an error that you have not bothered to describe to us, but having had some experience with serializing and de-serializing data before, I have an idea what it might be. First, I'd say that I see no obvious problem with your code.
When you serialize a data object, it is a refection of that class at that time. If you serialize a data object to a file and then make a change to that class, the serialized file will no longer represent that class... it will represent the class before the change(s), but not the current version of it. There would be no way to de-serialize that file using that edited class unless all of the changes were undone.
One way to test if you have this problem is to serialize another data object of the same type and then try to de-serialize it straight away. If it de-serializes ok then you have no problem with your code and the problem could well be that your class definition was changed since your object was serialized. If you can't de-serialize it, then you have a problem with your code.
Related
I am building a C# winforms application which needs to read in data from multiple files and allow the user to view/edit the data. There will be a large amount of data, so the user needs to be able to save their changes, close the program, and resume their work later.
I am struggling with the best approach for retaining this data after the user exits the program. I've followed a tutorial for data binding to objects, but in this tutorial the data is hardcoded into the Form_Load event and the changes are lost when you exit the program. The author alludes to preferring to use an object-based data source instead of a database for data binding, but doesn't describe on how/if he saves data after the user exits.
Is there a way to store the data in the object-based data source between sessions, without setting up a local database or manually writing to some type of file? Or must I set up a local database in order to save data?
As users Jimi and bhmahler mentioned in the comments, the concept I was looking for was Object Serialization.
I created the following method to save my data:
private void Serialize()
{
Stream s = File.Open("data.txt", FileMode.Open);
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = new List<Airplane>();
foreach (var a in bsAirplanes) //bsAirplanes is the bindingsource object
{
data.Add((Airplane)a);
}
b.Serialize(s, data);
s.Close();
}
And this method to load saved data:
private void Deserialize()
{
Stream s = File.OpenRead("data.txt");
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = (List<Airplane>) b.Deserialize(s);
foreach(Airplane a in data)
{
bsAirplanes.Add(a);
}
s.Close();
}
I also had to mark the Airplane class with the [Serializable()] attribute.
I'm basically trying to add podcasts to an XML file and eventually retrieve them from that same XML, using the data in different parts of the application.
I successfully write the data to the XML but every time I reboot the application (debug) and press the "submit" button the XML file resets to 0 entries.
The submit code:
PodcastList podList = new PodcastList();
private void btnAddPod_Click(object sender, EventArgs e)
{
var podUrl = txtAddPod.Text;
var podName = txtEnterName.Text;
podList.AddPod(podUrl, podName, 0);
Xml.SaveListData(podList.GetPodcastList(), "Podcasts.xml");
}
Save to XML:
public static void SaveListData(object obj, string filename)
{
var serializer = new XmlSerializer(obj.GetType());
using (var stream = new StreamWriter(filename))
{
serializer.Serialize(stream, obj);
}
}
I guess the applications creates a new XML-file every time I press submit and has fresh objects. What am I doing wrong? Cheers
XML files are not generally 'appended to' due to the need for opening and closing tags. As opposed to say, other types of text file like log files where appending makes a bit more sense.
When you call serializer.Serialize the whole file gets overwritten.
What your program needs to do is read in the already-existing XML file on startup and store that as a PodcastList(). Your program can then add to it (in memory) and save the whole list as a file.
PodcastList podList = new PodcastList(); is at the class level.
If you want to maintain the state , Reload the XML file Deserialize the file to PodcastList in the Constructor or at the time of the load, Then you will be able to retain and reuse the collection and rewrite the data back to XML file.
I am using a tabcontrol which I want to serialize and save . I am using this code but it gives that tabcontrol class is not marked as serializable . How to mark it serializable as I am not able to override the class . How to do it ?
using (Stream stream = File.Open("data.dat", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream,tabControl1);
}
It gives this error
System.Windows.Forms.TabControl not marked as serializable
Why don't serialize Controls and is there an alternative?
If you serialize a control, there are some problems:
You can't do it because System.Windows.Forms.TabControl not marked as serializable like you have seen.
If you will do it and only if it is allowed, are there a lot of properties and classes, interfaces, events etc. that are serialized with it, inherited from the classes above and that is not what you will.
The only way you could do it is by made a new class, bind all the values you will save with the properties and serialize that class.
[Serializable] // don't forget this! It will mark your class so you can serialize it.
public class BindingClass // p.s.: give this a better name!
{
public string Text { get; set; } // Bind whit a control of your tab control.
public float Number { get; set; }
public string ImageLocation { get; set; } // used for the image
public IEnumerable<object> ListOfString { get; set; } // used for a list
}
Code example
Text and numbers
Well for text and numbers it is easy. You can made an intense of your class and you can bind that. After it you can serialize it. An example:
BindingClass bc = new BindingClass();
bc.Text = textBox1.Text;
bc.Number = numbericUpDown.Value;
using (Stream stream = File.Open("data.dat", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream, bc);
}
Images
For images is it a little bit complex. You can serialize an image but it is also a bad thing to do that. Better is to save the image in your bin/debug folder of your project and serialize the path of that image. An example:
string imageLocation = Application.StartupPath + #"\myImage.jpg"
pictureBox1.Image.Save(imageLocation, ImageFormat.Jpeg);
// declare bc like code above.
bc.ImageLocation = imageLocation;
// serialize bc.
If the image already exists in the file, you can override it. But if you will work with histories, not a good thing... You can solve it by use the current date time as filename! Change your code with this:
string imageLocation = Application.StartupPath +
DateTime.Now.ToString("yyyyMMddHHmmss") + ".jpg"
Note: You can also use a blob service like Azure and Amazon (not free) or upload images to Imgur, Flickr or 9gag (patical free). Remark that there must be an internet connection between the client and server. You can upload by searching on Google how to do it.
List of string
For list of strings you can use this:
bc.ListOfString = comboBox1.Items;
Note
I haven't test the code. So if you have a problem with one the examples comment it and I will look at it, but try also to look on Google for a solution for your problem. Try it yourself, best way to learn...
Alternative for serialize (update 16-Jun-16)
Serializing is a save way to make your code unreadable for people. However that can give problems if you scale your application. The problem is also happen by Microsoft Word. The old .doc files are also serialized code, the new .docx files are zipped xml files and now it's easier to make .docx files.
Good alternatives are Json or XML.
I am making a basic platformer, my first ever game. I've run into a bit of a problem. So far the game only has one level, and it is loaded from a .txt file. However I'd like to have a sort of an Angry Birdish world/level selection screen.
The plan is to have an icon for each level visible, but only so far completed levels and the next one accessible. Also for the completed levels the score (stars, whatever) would be displayed under the icon.
I do not wish to load the levels from XML, at least not yet. Only the persistent world data that needs to be read AND written. I assume the easiest way is to load even the formatting of the level selection screen from XML, and not use the method i currently use (text files).
I could do this with text files, I suppose, but I really do not relish the idea of writing and sorting through the file. I then discovered that XML-files should be a bit less problematic in this regard. However additional problem rises from the fact tht I've never ever worked with XML-files before.
Could someone point me in a direction of a tutorial for this sort of things, or some sample you might have come accross that accomplishes at least relatively similar results. I don't expect anyone to do the coding for me, but if you have pointers or time and patience to provide a sample, I'd appreciate it a lot.
After some further digging and fumbling with tutorials for older XNA versions I managed to produce following save/load class:
namespace SaveLoadXML
{
class SaveLoad
{
public LevelInfo Load (int id)
{
LevelInfo level;
// Get the path of the save game
string fullpath = "World.xml";
// Open the file
FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate,
FileAccess.Read);
try
{
// Read the data from the file
XmlSerializer serializer = new XmlSerializer(typeof(LevelInfo));
level = (LevelInfo)serializer.Deserialize(stream);
}
finally
{
// Close the file
stream.Close();
}
return (level);
}
public void Save (LevelInfo level, int id)
{
// Get the path of the save game
string fullpath = "World.xml";
// Open the file, creating it if necessary
FileStream stream = File.Open(fullpath, FileMode.OpenOrCreate);
try
{
// Convert the object to XML data and put it in the stream
XmlSerializer serializer = new XmlSerializer(typeof(LevelInfo));
serializer.Serialize(stream, level);
}
finally
{
// Close the file
stream.Close();
}
}
}
}
Now I started to think, is there a way to target a specific part of the XML-file, or is the writing always just from the start? Almost all of the examples I saw had a condition at the start: if the file exists, delete it and then write.
I assume I could (or even should?) make a list of LevelInfo objects and just load them all at once, as there is no real need to load a single LevelInfo anyway. On the saving however, do I need to load the previous state (old list) and then manipulate the list regarding the certain indexes involved, and then delete te file, and save it again.
This might open an easy way for the system to fail if something goes wrong in the saving or power fails for example. The whole file would be lost or corrupt. I suppose this ould be countered with using back-up file and then checking the integrity of the main file, but now it's starting to feel like quite a mountain to climb for a beginner like me.
Having tried this question on GameDev, I'll just clarify the main question here:
1) Can I save only info about one or two levels in the XML-file containing info for all levels? ie. can I use some indexing to point the write operation to a particular section that would then be overwritten/replaced.
2) If not, is there any way to safely load all info from file, delete file, save all info after modifying it where needed.
After some looking into this Json stuff, I've managed to successfully serialize test level information. However, de-serialization fails as I have a rectangle as a part of the object. Error is as follows:
Error converting value "{X:1 Y:1 Width:1 Height:1}" to type 'Microsoft.Xna.Framework.Rectangle'. Path '[0].Rectangle', line 6, position 46.
class LevelInfo
{
public int ID { get; set; }
public Vector2 Dimensions { get; set; }
public Vector2 Position { get; set; }
public Rectangle Rectangle { get; set; }
public int Stars { get; set; }
public string Text { get; set; }
}
class SaveLoadJSON
{
public static List<LevelInfo> Load()
{
List<LevelInfo> levels = new List<LevelInfo>();
using (StreamReader file = File.OpenText("World.json"))
{
JsonSerializer serializer = new JsonSerializer();
levels = (List<LevelInfo>)serializer.Deserialize(file, typeof(List<LevelInfo>));
}
return levels;
}
public static void Save(List<LevelInfo> levels)
{
if (File.Exists("World.json"))
{
File.Delete("World.json");
}
using (FileStream fs = File.Open("World.json", FileMode.CreateNew))
using (StreamWriter sw = new StreamWriter(fs))
using (JsonWriter jw = new JsonTextWriter(sw))
{
jw.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(jw, levels);
}
}
}
Is there a way to work around this? Preferably a relatively simple way for a simple beginner like me.
Or alternatively, is there a way to omit the rectangle information to begin with, and maybe add it later? If I input nothing to the rectangle, it still is added to Json-file with 0 values. I do need the rectangle info for the drawing.
So here comes the promised answer.
Personally I'd prefer using JSon for storing data, since it's a lot easier to work with than XML, and takes up less storage. What you're going to want to do, is make Data Models of your player, enemy, items, scene objects, etc.
Then, you'll want to JsonConvert.SerializeObject() a parent data model, which will contain all those things.
Save this in any file, and Deserialize it again upon load, and reconstruct all objects from scratch.
Alternatively, just have all properties in the classes you're working with already, be public. That way, JsonConvert will be able to actually serialize the entire model. Keep in mind, if you do this runtime, it will make more of a complete snapshot of the Levels current state. Aka. where the enemies are located, the health remaining and whatever else you may have.
I hope this answers your question.
My application have a configuration xml-file. That file contains more than 50 program settings. At the present time I read and save each program setting separately. I guess It is not effiсiently for such tasks.
I need something that can auto-generate a code for load and save my program settings using predefined xml-schema.
I found a dataset in Add New Item dialog. Unfortunately, i cannot add new code to dataset1 such as events in set-accessors of properties because of this
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
Maybe, there is a tool that allows a user to generate a wrapper for accesing a xml-file ? Such as DataSet1, but with availability to add events.
Edit: I didn't mark a useful answer because i read an articles (link) which you give me. I will mark useful answer later.
If you are not willing to use app.config/web.config or the properties file (which Oded and Bruno recommend and I recommend as well), I highly recommend this utility:
Web Services Contract First (WSCF) Blue for VS2008 and VS2010
If you're on VS2005, you'll want this version of the tool: http://www.thinktecture.com/resourcearchive/tools-and-software/wscf (don't use the VS2008 version on this site. I could never get it to work right.)
Once you have the plugin installed into Visual Studio, you'll need an XSD schema of your XML file. (Google for an online XSD Generator.) Following the instructions found on the WSCF website, you can generate a wrapper class that will deserialize and reserialize your XML and give you an abstracted view of your XML.
I figure it is impossible (or at least very hard) to add new node/element TYPES, but adding new instances of existing node/element types, accessing to your data, editing the data, reordering nodes, and then saving back out are all easy.
Deserialization code looks like this:
private MyGeneratedXMLconfigClass config;
using (StreamReader sr = new StreamReader(filename))
{
XmlSerializer cXml = new XmlSerializer(typeof(MyGeneratedXMLconfigClass));
config = (MyGeneratedXMLconfigClass)cXml.Deserialize(sr);
}
Now your XML has been de-serialized into the "config" instance of your custom class. Then you can access the whole class as a series of nested values and Lists.
For example:
string errorFile = config.errorsFile;
List<string> actions = config.actionList;
var specialActions = from action in config.actionList
where action.contains("special")
select action;
Etc., etc. Then once you're done manipulating your data, you can re-serialize with this code:
using (StreamWriter wr = new StreamWriter(filename, false))
{
XmlSerializer cXml = new XmlSerializer(typeof(MyGeneratedXMLconfigClass));
cXml.Serialize(wr, config);
}
One of the very nice things about this tool is that it auto-generates all classes as "partial" classes, so that you can feel free to extend each class on your own without fear of your code getting stomped on in case you ever need to re-generate because the XSD/XML was changed.
I imagine this might sound like a lot, but the learning curve is actually pretty easy and once you get it installed and working, you'll realize how stupidly easy it is. It's worth it. I swear. :-)
If you have an appropriate xsd schema for your xml file microsoft provides xsd.exe, a small tool which auto-generates c# classes for this schema.
For details see: http://msdn.microsoft.com/en-us/library/x6c1kb0s%28VS.71%29.aspx
Why are you using hand rolled XML for configuration? What is wrong with the existing app.config and web.config schemas?
Why not use a .Settings file?
You can follow these steps:
1) generate an XSD file from your XML file. For There used to be a tool to infer schema from an XML file, I forgot what it's called. Currently I use my own utility, which basically runs this core routine to read an xml file and generate the corresponding xsd:
static void InferSchema(string fileName)
{
XmlWriter writer = null;
XmlSchemaInference infer = new XmlSchemaInference();
XmlSchemaSet sc = new XmlSchemaSet();
string outputXsd = fileName.Replace(".xml", ".xsd");
sc = infer.InferSchema(new XmlTextReader(fileName));
using (writer = XmlWriter.Create(new StreamWriter(outputXsd)))
{
foreach(XmlSchema schema in sc.Schemas())
{
schema.Write(writer);
Console.WriteLine(">> found schema - generated to {0}",
outputXsd);
}
}
}
2) run xsd.exe to generate a serializable class from the XSD file.
xsd.exe /c /n:MyNameSpaceHere MyGenerated.xsd
Next, you can read the XML file into the serializable class using XmlSerializer.Serialize() method. Something like this:
public static void Serialize<T>(T data, TextWriter writer)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.Serialize(writer, data);
}
catch (Exception e)
{
throw;
}
}
Finally, you can write back to the XML file from the class using the XmlSerializer.Deserialize() method, like this for instance:
public static void Deserialize<T>(out T data, XmlReader reader)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
data = (T)xs.Deserialize(reader);
}
catch (Exception e)
{
reader.Close();
throw;
}
}
This is called a properties file. C# should have something similar to Java's Properties class where you can load all properties without hard-coding their names.
EDIT:
There's apparently no built-in properties parsing solution for C#. But you can easily implement your own. See here.
If you have an XSD file, you can generate classes from that. Besides the already mentioned xsd.exe from Microsoft (which hasn't been updated for quite some time), there are other tools for this. I am using XSD2Code, which allows generating strongly typed collections, lazy initialization, etc.
If you do not have an XSD, you can point the xsd.exe at your xml-file, and it will generate an XSD from that. The schema usually needs some work, but generally is a good starting point.
xsd.exe (instance).xml
You can use System.Xml.Serialization - it's very easy and you can serialize even class objects directly, like MyCustomClass (it even saves MyCustomClass public fields).
Deserializing the XML file will get a new instance of MyCustomClass, so such a feature is priceless.
Note one thing: you should add EVERY SINGLE TYPE you use in the class, but that's easy though.
I attached a complete project that does the thing you want. just change classes and objects and that will be all.
source code
for example (i'm shortening the code):
using System.Xml;
using System.Xml.Serialization;
using System.IO;
[XmlRootAttribute("Vendor")]
class Vendor{
[XmlAttribute]
Product prod;
}
[XmlRootAttribute("Product")]
class Product{
[XmlAttribute]
public string name="";
}
class Test{
Vendor v=new Vendor();
Product p=new Product();
p.name="a cake";
v.prod=p;
//add EVERY SINGLE TYPE you use in the serialized class.
Type[] type_list = { typeof(Product) };
XmlSerializer packer = new XmlSerializer(v.GetType(),type_list);
XmlWriter flusher = XmlWriter.Create(#"c:\bak.xml");
packer.Serialize(flusher, v);
flusher.Close();
XmlReader restorer = XmlReader.Create(#"c:\bak.xml");
Vendor v2 = (Vendor)packer.Deserialize(restorer);
//v2.prod.name is now "cake"
//COOL was my first impression :P
}