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).
Related
I have something like this:
public class MyClass_T
{
public string filename;
public string filename_txt;
public int version = 1;
public double A;
public double B;
....and so on with about 100 more variables
}
I've written the data to a file in JSON format with
public bool readFormatFile(string filename)
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
using (FileStream fs = new FileStream(filename, FileMode.Create, System.IO.FileAccess.Write, FileShare.Read))
{
using (StreamWriter sw = new StreamWriter(fs))
{
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, this);
}
}
}
}
Now I want to deserialize it. I know I can do this:
public bool writeFormatFile(string filename)
{
MyClass_T MC = new MyClass_T();
using (FileStream fs = new FileStream(filename, FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
MC = (MyClass_T)serializer.Deserialize(sr, typeof(MyClass_T));
}
}
}
Note that readFormatFile and writeFormatFile are part of MyClass_T. I need to get the values back into my local variables without having to do a bunch of
filename = MC.filename;
filename_txt = MC.filename_txt;
version = MC.version;
A = MC.A;
B = MC.B;
...and so on for the 100 or so variables.
Thoughts and ideas on how to proceed on this?
Like many people have made clear, it is not a very good idea to deserialize a JSON object to local variables, however if you can not create a class for the object, you can use the dynamic object type, this will allow you to deserialize the JSON into an object whilst removing the need to add any strongly types models/classes.
all you need to do is cast the deserialized result to a dynamic as you would any other object, e.g.
dynamic myObject = (dynamic)serializer.Deserialize(sr, typeof(dynamic));
this will allow you to then access myObject as you would any other object e.g.
myObject.filename for example.
i would also like to reiterate that, you really should be using a class for this as it makes code a lot more predictable, maintainable and clean in general
Instead of using an instance 'ReadFormatFile' method, you could add a static method 'ReadFormatFile' to the 'MyClass_T' class that returns the 'MyClass_T' instance that is returned from the serializer. And then you could use that method instead of calling the 'ReadFormatFile' method (or using 'new' to instantiate a 'MyClass_T' instance that is populated with the information that was previously serialized to the file).
Consider the following class:
class Demo
{
public double A;
public double B;
public void WriteFormatFile(string filename)
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
using (FileStream fs = new FileStream(filename, FileMode.Create, System.IO.FileAccess.Write, FileShare.Read))
{
using (StreamWriter sw = new StreamWriter(fs))
{
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, this);
}
}
}
}
public static Demo ReadFormatFile(string filename)
{
using (FileStream fs = new FileStream(filename, FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.MissingMemberHandling = MissingMemberHandling.Ignore;
return (Demo)serializer.Deserialize(sr, typeof(Demo));
}
}
}
}
and its use elsewhere:
const string filename = "demo.json";
var d = new Demo();
d.A = 2;
d.B = 3;
d.WriteFormatFile(filename);
d.A = 4;
// replace d.ReadFormatFile(filename); with the following line
d = Demo.ReadFormatFile(filename);
Console.WriteLine(d.A);
The output will be '2', the restored value for the 'A' field.
I got a weird problem where I can serialize a derived class if I put it in a List but not when it stands on it own.
I am using these methods to serialize and deserialize (found them here):
public static string Serialize(T obj)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
public static T Deserialize(string xml, Type toType)
{
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
DataContractSerializer deserializer = new DataContractSerializer(toType);
return (T)deserializer.ReadObject(stream);
}
}
I have a base class Entity. And a derived class Thing.
[DataContract]
[KnownType(typeof(Thing))]
Class Entity
{
}
[DataContract]
Class Thing : Entity
{
}
Now, when I try to serialize an object instantiated as Thing there is no problem but the deserialization gives an error.
Thing e = new Thing();
string xmlstring = XML<Entity>.Serialize(e);
Entity testE = XML<Entity>.Deserialize(xmlstring, typeof(Entity));
The error says something like Expected element Entity from ... . Element Thing was found. (Was not written in english.)
But the weird thing is that it works if I put the object into a list and serialize-deserialize the list.
Thing e = new Thing();
List<Entity> test = new List<Entity>();
test.Add(e);
var xmlstring = XML<List<Entity>>.Serialize(test);
List<Entity> test2 = XML<List<Entity>>.Deserialize(xmlstring, typeof(List<Entity>));
Now test2 has an entry with the a correct Thing-item. Even if this could be a workaround it surely must be an easier way and it must be something that would be easy to fix I think? Any thoughts? What did I miss?
(And of course it works when deserializing my Thingobject as a Thing-object but that's not what I want, when deserializing I will not know the class on beforehand.)
Seems that I have found a workaround myself after thinking about #Jaya's comment. I modified the serialize method to force it to serialize to the base class type. Originally I did not think this would work, but it did!
public static string Serialize(T obj, Type toType)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
DataContractSerializer serializer = new DataContractSerializer(toType);
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
I was able to serialize a List of objects (List) using this code:
public static string Serialize(object obj)
{
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
{
DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
However, I'm not able to deserialize using this code:
public static object Deserialize(string xml, Type toType)
{
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
DataContractSerializer deserializer = new DataContractSerializer(toType);
return deserializer.ReadObject(stream);
}
}
I'm not able to understand the problem.
I'm using the last method by calling it with:
Deserialize(SerializedObject, List), but I'm getting an error saying List<FilesToProcess> is a type, which is not valid in the given context
Could anyone help? I'm a bit over my head with this.
Sooo, I have the doubtful honor of answering my own question.
The problem was that I was trying to assign the output of
public static object Deserialize(string xml, Type toType)
to a List generic called listOfFiles, when I should have assigned to an object and then cast to a List<FilesToProcess> using
List<FilesToProcess listOfFiles = (List<FilesToProcess)listOfFilesObject;
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.
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);.