I am using Binary Formatter to save/load data from a file. I have a library system, with two concrete classes - Users and Items - and an abstract class - Library. I am also using two lists:
List<Item> items = new List<Item>();
List<User> users = new List<User>();
public static void Serialize(object value, string path)
{
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, value);
}
}
public static object Deserialize(string path)
{
if (!System.IO.File.Exists(path)) { throw new NotImplementedException(); }
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = File.OpenRead(path))
{
return formatter.Deserialize(fStream);
}
}
}
Above are the two methods that I'm using to save and load.
To call them from the Program file, I am using this code for saving:
string pathItem1 = #"itemList";
string pathUser1 = #"userList";
Library.Serialize(Library.myItems, pathItem1);
Library.Serialize(Library.myUsers, pathUser1);
Console.WriteLine("Serializing...");
and this code for loading:
string pathItem = #"itemList";
string pathUser = #"userList";
//var deserializedItem
List<Item> items= (List<Item>)Library.Deserialize(pathItem);
//var deserializedUser =
List<User> users = (List<User>)Library.Deserialize(pathUser);
Console.WriteLine("Deserializing...");
Saving seems to work fine. Loading however isn't. I am getting this error message:
Additional information: Unable to cast object of type 'System.Collections.Generic.List1[LibrarySystem.User]' to type 'System.Collections.Generic.List1[LibrarySystem.Item]'.
Thanks!
You have a strange code in your Serialize method. You are saving value parameter to both pathes: "itemList" and "userList". And actually you are not using path parameter. Your code should looks like this:
public static void Serialize(object value, string path)
{
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, value);
}
}
With this implementation your code will work as expected.
public static void Serialize(object value, string path)
{
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = new FileStream(#"itemList", FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, value);
}
using (Stream fStream = new FileStream(#"userList", FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, value);
}
}
You're storing value in both userList and itemList, despite passing the path as a parameter. And since the last call is Library.Serialize(Library.myUsers, pathUser1), what's in itemList is a list of User objects -- exactly what the error is telling you.
The fix is obvious, simply use the passed path, however you should consider a more robust state storing system. For example you can have a class that contains both lists and you serialize that.
And skipping over a few relatively obvious improvements (Deserialize is a prime candidate for a generic function so you don't have to cast it every time for example), one important note is that NotImplementedException isn't something vague you can tack your own meaning to, it has a very precise meaning in the context of the .Net framework, and that's a function in your code that you didn't implement for various reasons. The "generic" exception you want is either ArgumentException or InvalidOperationException.
Related
In C# Dot Net, How to handle a exception when you want to de-serialize a xml file, but by default the file doesn't exists! because you have to run the program to create one.
Below is the area where I need Help.
public static Compare_Data[] Deserialize()
{
Compare_Data[] cm;
cm = null;
string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
XmlSerializer xs = new XmlSerializer(typeof(Compare_Data[]));
if (File.Exists(path))
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
// This will read the XML from the file and create the new instance of Compare_Data.
cm = (Compare_Data[])xs.Deserialize(fs);
return cm;
}
}
else
{
using (FileStream fs = new FileStream(path, FileMode.Create))
{
xs.Serialize(fs); /// what to add here ?
}
}
return null;
}
If general, you don't want your methods to have side effects. In this case, creating an empty log file in the else branch is probably unnecessary and should be handled by a separate Serialize() method when there is data to be logged.
Your code could be simplified something like this:
public static Compare_Data[] Deserialize()
{
const string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
if (!File.Exists(path))
{
// return null or an empty array, depending on how
// you want the calling code to handle this.
return null;
}
using (FileStream fs = new FileStream(path, FileMode.Open))
{
var xs = new XmlSerializer(typeof(Compare_Data[]));
return (Compare_Data[])xs.Deserialize(fs);
}
}
I'm getting System.IO.IOException because my file being used by another process. Is it because of unclosed stream? If yes how can I close it?
public static ReportClass DeserializeRep(string FileWay)
{
Stream stream = File.Open(FileWay, FileMode.Open);
BinaryFormatter bformatter = new BinaryFormatter();
return (ReportClass)bformatter.Deserialize(stream);
}
var CurRep = RequestSerializer.DeserializeRep(paths[selected]);
You should use the using statement:
public static ReportClass DeserializeRep(string FileWay)
{
using (Stream stream = File.Open(FileWay, FileMode.Open))
{
BinaryFormatter bformatter = new BinaryFormatter();
return (ReportClass)bformatter.Deserialize(stream);
}
}
It should also be noted that the using statement automatically calls the Dispose method of any object that inherits from IDisposible, which in this case closes the connection and then disposes the object.
IDisposible Documentation can be found here.
I've got a List of Types that I need to save to file and read it after.
I use DataContractSerializer but I get an exception during deserialization:
Can't find constructor with arguments (SerializationInfo,
StreamingContext) in ISerializable "System.RuntimeType".
I've added System.RuntimeType as a known type to my serializer, but it didn't help.
Here's code of my two methods
public static void SaveTypes(List<Type> types, string fileName)
{
Type rt = types[0].GetType();
List<Type> knownTypes = new List<Type>() { rt }; //I get a List with System.RuntimeType item here
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Type>), knownTypes);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
Stream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
using (XmlWriter xw = XmlWriter.Create(fs, settings))
serializer.WriteObject(xw, types);
}
Serialization seems to work fine, and the output file is ok, but problem starts on deserializing:
public static object LoadTypes(string fileName)
{
Stream file = new FileStream(fileName, FileMode.Open, FileAccess.Read);
byte[] data = new byte[file.Length];
file.Read(data, 0, (int)file.Length);
Type rt = file.GetType();
List<Type> knownTypes = new List<Type>() { rt.GetType() };
DataContractSerializer deserializer = new DataContractSerializer(typeof(List<Type>), knownTypes);
Stream stream = new MemoryStream();
stream.Write(data, 0, data.Length);
stream.Position = 0;
return deserializer.ReadObject(stream); //exception here
}
Is there any way to go through this? Or maybe there's some other way to store types?
Marc Gravell is right, you probably should be serializing the data and not the types.
But for some reason, if you really want to serialize the types themselves, then you shouldn't serialize the Type object (pretty sure it's not serailizable). Anyway, serialize Type.FullName instead. When you load the Types, use Type.Load
public static void SaveTypes(IEnumerable<Type> types, string filename)
{
using (var fs = File.Open(filename, FileMode.OpenOrCreate)
new XmlSerializer(typeof(string[]))
.Serialize(fs, types.Select(t => t.FullName).ToArray())
}
public static IEnumerable<Type> LoadTypes(string filename)
{
using (var fs = File.Open(filename, FileMode.Open)
{
var typeNames = (string[])
new XmlSerializer(typeof(string[]))
.Deserialize(fs);
return typeNames.Select(t => Type.Load(t));
}
}
Note: When working with any Stream (or really any IDisposable) you have to either call the Dispose method or use the using statement (as I did above). This ensures that the IDisposable is properly cleaned up (ie releases File System handles).
public static List<Restaurant> LoadRestaurantList()
{
FileStream fs = new FileStream("Restaurant.txt", FileMode.OpenOrCreate);
BinaryFormatter bf = new BinaryFormatter();
List<Restaurant> rest =(List<Restaurant>)bf.Deserialize(fs);
fs.Close();
return rest;
}
I have Serailze the generic list which I have, into "Restaurant.txt" file.
now I want to Deserialize the same and return it into a Generic List, I have tried
but its not working and it is giving error "Invalid Cast Expression".
This is the Serialization code:
public static void SaveRestaurantList(List<Restaurant> restaurantList)
{
FileStream fs = new FileStream("Restaurant.txt", FileMode.Create, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
for (int i = 0; i < restaurantList.Count; i++)
{
Restaurant r = new Restaurant();
r = (Restaurant)restaurantList[i];
bf.Serialize(fs, r);
fs.Flush();
}
fs.Close();
}
Can anyone please help in solving out this.
Serialization and Deserialization are each others opposites. This means the type(s) used during serialization needs to be the same during deserialization.
In your code that is not the case. You serialize Restaurant types but when you deserialize you expect a List.
Adapt your serialization code as follows:
public static void SaveRestaurantList(List<Restaurant> restaurantList)
{
using(FileStream fs = new FileStream("Restaurant.txt", FileMode.Create, FileAccess.Write))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, restaurantList);
}
}
I have a generic class as follows:
class myClass<T>
{
public T[] m_SomeData;
}
I want to implement a generic method to read data from a file and populate the data fields of this class. Something like:
class myClass<T>
{
public T[] m_SomeData;
public void ReadData(string fileName);
}
An implementation of the ReadData methods looks something like this (all error checking removed for brevity):
void ReadData(string fileName)
{
TextReader rdr = new StreamReader(fileName);
string line = rdr.ReadLine();
// Here I need to parse value of type T from the line
// and initialize the m_SomeData array
// How do I do that? I would like to keep it generic if possible
}
Note, I can guarantee the type T is numeric, at least by convention
Update: OP would like human readable output. I would suggest JavaScriptSerializer, then, in:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Web.Extensions.dll
// Serialize:
using (var fs = new FileStream(fileName, FileMode.Create))
using (var writer = new StreamWriter(fs))
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
string s = serializer.Serialize(m_SomeData);
writer.Write(s);
}
// Deserialize:
using (var fs = new FileStream(fileName, FileMode.Open))
using (var reader = new StreamReader(fs))
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
var s = reader.ReadToEnd();
m_SomeData = serializer.Deserialize<T[]>(s);
}
Old Answer:
This is a job for BinaryFormatter:
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
m_SomeData = (T[])formatter.Deserialize(fs);
}
This of course assumes you are also using it to serialize via formatter.Serialize(fs, m_SomeData);.