I have a class that handles serialization in C#, called Serializer. It's implementation is below:
public class Serializer
{
public void SerializeRulesManager(string filename, RulesManager rulesManager)
{
Stream stream = File.Open(filename, FileMode.Create);
try
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, rulesManager);
}
finally
{
stream.Close();
}
}
public RulesManager DeserializeRulesManager(string filename)
{
RulesManager rulesManager = null;
Stream stream = File.Open(filename, FileMode.Open);
try
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
rulesManager = (RulesManager)binaryFormatter.Deserialize(stream);
}
finally
{
stream.Close();
}
return rulesManager;
}
}
Pretty straightforward stuff, and it works just fine with all of my unit tests. The RulesManager is correctly serialized and deserialized so I know the graph is good.
The trouble comes with the following code:
public void Save(string filename)
{
Cursor.Current = Cursors.WaitCursor;
try
{
_serializer.SerializeRulesManager(filename, _rulesManager);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
Cursor.Current = Cursors.Default;
}
}
That function is part of the Manager class. The Manager class is instantiated on the MainForm. The MainForm uses a SaveFileDialog to prompt the user for the filename and location they want to save to and then makes the following call:
saveFileDialog.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath);
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
_manager.Save(saveFileDialog.FileName);
}
Thus calling the function above. When it does so, I get the following exception in Serialize.SerializeRulesManager at the binaryFormatter.Serialize(stream, rulesManager) line:
Type 'TestHarness.MainForm' in Assembly 'TestHarness, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Why would MainForm need to be marked as Serializable? Just for kicks, I put the Serializable attribute on MainForm and it just moved the exception up one level to say that Windows.Form was not marked as Serializable. What gives?
RulesManager probably has a reference to MainForm. If so, mark it as not serialized with the
NonSerializedAttrbibute
Related
I do not know if this is even possible but let's suppose I have a class:
[Serializable]
public class Test
{
//Properties and functions would go here.
public void SaveClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.Create(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, this);
stream.Close();
}
public void LoadClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.OpenRead(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
this = binaryFormatter.Deserialize(stream); //It returns an error saying that 'this' is readonly.
stream.Close();
}
}
Now I want to be able to Save and Load the class. The SaveClass is working fine, but the LoadClass returns an error, because I can not assign anything to this. Is there a way that I could load the class from a file, or is this impossible.
Make LoadClass static, something like:
public static Test LoadClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.OpenRead(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
var newTest = (Test)binaryFormatter.Deserialize(stream);
stream.Close();
return newTest;
}
I have a base class like :
public class Sensor
{
public void Serialize(string path)
{
try
{
System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(this.GetType());
using (System.IO.StreamWriter file = new System.IO.StreamWriter(System.IO.Path.GetFullPath(path)))
{
xml.Serialize(file, this);
}
}
catch (Exception e)
{
;
}
}
public static T Deserialize<T>(string path)
{
T loaded = default(T);
try
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (StreamReader reader = new StreamReader(path))
{
loaded = (T)deserializer.Deserialize(reader);
}
}
catch (Exception e)
{
;
}
return loaded;
}
}
Then I have a couple of classes that derive from this:
public TemperatureSensor : Sensor {}
public PositionSensor :Sensor{}
They share some common interfaces but also implement things differently.
I have a SensorController that contains a List<Sensor> with a mixture of different sensors. I want to save them to XML files and load them afterwards.
I tried a simple:
public void Load()
{
var files = Directory.GetFiles(directory, "*.xml");
foreach(var file in files)
{
var p = CodePuzzle.Deserialize<Puzzle>(file);
}
}
The problem is that when the deserializer finds the <PositionSensor> it crashes (Unexpected <PositionSensor> at 2,2).. I guess it was expecting <Sensor>
How can that be done?? Loading each Sensor in the sub-class it was originally stored in??
First you should add the tag [XmlInclude(typeof(DerivedClass))] to the base Class. So it looks like:
[Serializable]
[XmlInclude(typeof(TemperatureSensor))]
[XmlInclude(typeof(PositionSensor))]
public Class Sensor
....
Then when you save to xml file any of the derived class, save them as Sensor, not as the derived class.
var myTemp = new TemperatureSensor();
Sensor.saveToXML(myTemp,"myTemp.xml")
Then when reading the xml´s in as Sensor, you can still identify the subclass they actually belong to.
Sensor myImportedSensor = Sensor.Load("myTemp.xml")
// myImportedSensor is Sebsir returns true
// AND
// myImportedSensor is TemperatureSensor returns also true
I tried XML serialization the first time. The following code works as expected, but I don't like to use this "copyFrom" method.
Is there a better way that keeps the save and load methods inside the class itself?
namespace Test
{
[Serializable]
public class Settings
{
public struct Connection
{
[XmlAttribute ("user")]
public string sUser;
[XmlAttribute ("domain")]
public string sDomain;
}
public Connection connection;
public Settings ()
{
connection.sUser = "";
connection.sDomain = "";
}
internal void loadFromFile ()
{
if (File.Exists (Constants.STORAGE_SETTINGS_FILE))
{
using (FileStream filestream = new FileStream (Constants.STORAGE_SETTINGS_FILE, FileMode.Open, FileAccess.Read, FileShare.Read))
{
copyFrom ((Settings)new XmlSerializer (typeof (Settings)).Deserialize (filestream));
}
}
}
internal void saveToFile ()
{
using (StreamWriter streamwriter = new StreamWriter (Constants.STORAGE_SETTINGS_FILE))
{
new XmlSerializer (typeof (Settings)).Serialize (streamwriter, this);
}
}
internal void copyFrom (Settings settings)
{
connection.sUser = settings.connection.sUser;
connection.sDomain = settings.connection.sDomain;
}
}
}
You can remove copyFrom method.
In the loadFromFile method, you can write
connection = ((Settings)new XmlSerializer(typeof(Settings)).Deserialize(filestream)).connection;
Also note that the Serializable attribute not needed for XML serialization. Remove it.
I am trying to move the m_settings variable from the volatile to the persistent records. I have tried adding the [serializable] attribute to the class and sending the m_settings variable to a file stream using a BinaryFormatter but I got an error saying that the file cannot be written, access to the file is denied. What am I doing wrong?
[Serializable]
public class SettingsComponent : GH_Component
{
public SettingsComponent(): base("LoadSettings", "LoadSettings", "Loading ini", "Extra", "Silkworm") { }
public override void CreateAttributes()
{
m_attributes = new SettingsComponentAttributes(this);
}
string m_settings_temp;
string[] m_settings;
public void ShowSettingsGui()
{
var dialog = new OpenFileDialog { Filter = "Data Sources (*.ini)|*.ini*|All Files|*.*" };
if (dialog.ShowDialog() != DialogResult.OK) return;
m_settings_temp = File.ReadAllText(dialog.FileName);
m_settings = m_settings_temp.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
ExpireSolution(true);
}
protected override void SolveInstance(IGH_DataAccess DA)
{
if (m_settings == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "You must declare some valid settings");
return;
}
else
{
FileStream fs = new FileStream("DataFiletemp.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, m_settings);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
DA.SetDataList(0, m_settings);
}
}
Arthur, if your m_settings object is not complex you can use project settings (application or user level). Here is sample code how you can save some project settings to app.config:
[YourNamespace].Properties.Settings.Default["MyProperty"] = "Demo Value";
[YourNamespace].Properties.Settings.Default.Save();
And if you have the need for binary serialization you can use code like this:
(serialized object and object of inheritance must be marked as serializable)
/// <summary>
/// Serializes object to file
/// </summary>
/// <param name="data"></param>
/// <param name="FilePath"></param>
public static void SerializeMyObject(object data, string FilePath)
{
System.IO.Stream stream = null;
try
{
stream = System.IO.File.Open(FilePath, System.IO.FileMode.Create);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bformatter =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bformatter.Serialize(stream, data);
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
try
{
stream.Close();
stream.Dispose();
}
catch (Exception)
{
}
throw new Exception(ex.Message);
}
}
I'm getting an exception with this code;
InnerException: System.InvalidOperationException
Message=The specified type was not recognized: name='Person',
namespace='', at "<"Contact xmlns=''>.
Here is the relevant code I think.
The class Person is a bare class without any anotations and doesn't inherit from any interface.
How to make Deserialize recognize my classes?
Thanks in advance.
public class Contacts : List<Contact.Contact>
{
private void PopulateTypeList()
{
types.Add(typeof(Contact.Contact));
types.Add(typeof(Contact.Company));
types.Add(typeof(Contact.Person));
types.Add(typeof(ContactData.Direction));
types.Add(typeof(ContactData.email));
types.Add(typeof(ContactData.Phone));
}
public void Load()
{
try
{
using (System.Xml.XmlReader stream = System.Xml.XmlReader.Create(fileName))
{
XmlSerializer xs = new XmlSerializer(typeof(List<Contact.Contact>));
// this roundabout way is for making it possible for this class to
// inherit from List<Contact.Contact> and still use a method that
// gives the stored data as an value in the object
here is error List<Contact.Contact> data =
(List<Contact.Contact>)xs.Deserialize(stream);
this.Clear();
this.AddRange(data);
}
}
catch (System.IO.FileNotFoundException)
{
// do nothing; no file, new database
}
}
public void Save()
{
using (System.Xml.XmlWriter stream = System.Xml.XmlWriter.Create(fileName))
{
XmlSerializer xs =
new XmlSerializer(typeof(List<Contact.Contact>), types.ToArray());
List<Contact.Contact> data = this.ToList();
xs.Serialize(stream, data);
}
}
Try passing the list of types to the serialiser you create for deserialisation:
XmlSerializer xs = new XmlSerialiser(typeof(List<Contact.Contact>), types.ToArray());