Deserialisation doesn't rebuild dictionaries that are members of classes - c#

I've got some serialisation code set up as follows:
static void SerialiseObject(Object o, String path)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(path, FileMode.Create);
formatter.Serialize(stream, o);
stream.Close();
}
static Object DeserialiseObject(String path)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
Object o = (Object)formatter.Deserialize(stream);
stream.Close();
return o;
}
And a class with the following member defined:
[Serializable]
public class CircuitModel
{
public Dictionary<String, Bus> Buses { protected set; get; }
...
}
I populate the Dictionary, and then the following code successfully serialises and deserialises the dictionary, with all Bus objects intact:
SerialiseObject(CircuitModel.Buses, "temp.bin");
Object o = DeserialiseObject("temp.bin");
But when I try to do the same for CircuitModel:
SerialiseObject(CircuitModel, "temp.bin");
Object o = DeserialiseObject("temp.bin");
CircuitModel.Buses has been initialised, but is empty.
I've also tried implementing serialisation with ISerializable (for the Bus and CircuitModel classes) and had exactly the same problem
Any idea as to why this would be happening?

I think you have something more sinister going on with your child collection because binary serialization of Dictionaries within classes does work just fine.
[TestFixture]
public class SerializeTest
{
[Test]
public void TestSer()
{
var parent = new Parent
{
Name = "Test"
};
parent.Children.Add("Child1", new Child {Name = "Child1"});
parent.Children.Add( "Child2", new Child { Name = "Child2" } );
SerialiseObject(parent, "test.bin");
var copy = DeserialiseObject("test.bin") as Parent;
Assert.IsNotNull(copy);
Assert.AreEqual(2, copy.Children.Count);
Assert.IsTrue(copy.Children.ContainsKey("Child1"));
Assert.AreEqual("Child1", copy.Children["Child1"].Name);
}
static void SerialiseObject( Object o, String path )
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream( path, FileMode.Create );
formatter.Serialize( stream, o );
stream.Close();
}
static Object DeserialiseObject( String path )
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream( path, FileMode.Open, FileAccess.Read );
Object o = (Object) formatter.Deserialize( stream );
stream.Close();
return o;
}
[Serializable]
private class Parent
{
public string Name { get; set; }
public Dictionary<string, Child> Children { get; protected set; }
public Parent()
{
Children = new Dictionary<string, Child>();
}
}
[Serializable]
private class Child
{
public string Name { get; set; }
}
}
The children deserialize with the parent and contain the details they were initialized with. I would check any code that is setting your Buses collection. My example just did it in the constructor of the parent class, but it may be possible that you have rogue code setting it after it's been deserialized?

Dictionaries are not serializable. Remove the dictionary if you need to serialize that data, and replace it by a list of a custom class that contains the data in the dictionary:
[Serializable]
public class BusItem
{
public string Name {get;set;}
public Bus Bus {get;set;}
}
Edit: I just found out you can actually serialize Dictionaries using the DataContractSerializer instead.
http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/

If you are talking about XML serialization, it might be because Dictionary is not serializable to XML. Look at Why isn't there an XML-serializable dictionary in .NET.

Related

BinaryFormatter determine type of object in OnDeserializingMethod

I have a very complex csla object. This object has been serialized to Database in binary. One of the child objects changes and I cannot deserialize the object anymore. (When I try to deserialize it, the properties contain junk or are not initialized because the deserialization did'nt work in the new class.) I'll post a simplified example here.
[serializable]
public class Head : BusinessBase<Head>
{
private static PropertyInfo<int> _eyesCountProperty = RegisterProperty<int>(new PropertyInfo<int>("EyesCount"));
public int EyesCount
{
get { return GetProperty(_eyesCountProperty ); }
set { SetProperty(_eyesCountProperty , value); }
}
}
[serializable]
public class Person : BusinessBase<Person>
{
private static PropertyInfo<string> _firstNameProperty = RegisterProperty<string>(new PropertyInfo<string>("FirstName"));
public string FirstName
{
get { return GetProperty(_firstNameProperty ); }
set { SetProperty(_firstNameProperty , value); }
}
}
public Head MyHead { get; set; }
}
So let's say I have an instance of "Person" class and I serialize it to the database.
Here's our serializing and deserializing methods.
public static byte[] ConvertToByteArray(object theObject)
{
byte[] result = null;
BinaryFormatter bf = new BinaryFormatter();
using(MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, theObject);
result = ms.ToArray();
}
return result;
}
public static object ConvertFromByteArray(byte[] serializedObject)
{
object result = null;
using(MemoryStream ms = new MemoryStream(serializedObject))
{
BinaryFormatter bf = new BinaryFormatter();
result = bf.Deserialize(ms);
}
return result;
}
Now let's say that the Head class changes and I now have the noseCount and mouthCount in it. So I try to rename it as "HeadV1" and create a "HeadV2" class with the new properties in it. (I would need to do a "PersonV1" class with the "HeadV1" property and a "PersonV2" class with the "HeadV2".)
In the PersonV2 class, I would like to have an "OnDeserializing" method that would let me know if the item I'm trying to deserialize is of type "PersonV1" or "PersonV2" in order to deserialize it the right way.
[OnDeserializing()]
internal void OnDeserializingMethod(StreamingContext context)
{
// Determine if data is of type "PersonV1" or "PersonV2"
}
But I'm stuck, I don't know how to do it and I can't seem to find how to do it. Is there any way to do so? I don't seem to have access to the data inside the "OnDeserializing" method?

Xml being deserialized into base class instead of derived classes

I know this is a popular topic and I have researched extensively without finding an answer to my problem.
I have a base class IntroductionAction and 2 derived classes IntroductionActionComplex and IntroductionActionSimple. I have a list of IntroductionAction objects to which I have added objects of both of the derived types. My classes are as follows:
[XmlInclude(typeof(IntroductionActionComplex))]
[XmlInclude(typeof(IntroductionActionSimple))]
public class IntroductionAction
{
public IntroductionAction() { }
}
public class IntroductionActionComplex : IntroductionAction
{
[XmlIgnore]
public string name { get; set; }
[XmlElement(ElementName = "QuestionString")]
public string question { get; set; }
[XmlElement(ElementName = "AnswerString")]
public List<string> answerStrings { get; set; }
public IntroductionActionComplex()
{
name = string.Empty;
question = null;
answerStrings = new List<string>();
}
}
public class IntroductionActionSimple : IntroductionAction
{
[XmlIgnore]
public string name { get; set; }
[XmlText]
public string Value { get; set; }
public IntroductionActionSimple()
{
Value = string.Empty;
}
}
I then create the List as follows
[XmlElement("IntroductionAction")]
public List<IntroductionAction> introductionActions { get; set; }
I am using XmlSerializer and everything serializes correctly. This is the resulting XML of the list containing one of each of the derived classes which is correct.
<IntroductionAction>
<QuestionString>
test
</QuestionString>
<AnswerString>
test
</AnswerString>
<AnswerString>
test
</AnswerString>
</IntroductionAction>
<IntroductionAction>
test
</IntroductionAction>
This XML file is going onto a device which doesn't read it as XML but just searches for the tags and does whatever work it needs to do and because of that the file can't contain any XSI or XSD tags, indentation, etc that is usually associated with proper XML.
My deserialization code is straight forward:
public static T Deserialize_xml_Config<T>(string file1, T obj)
{
XmlSerializer deserializer = new XmlSerializer(obj.GetType());
using (TextReader reader = new StreamReader(file1))
{
return (T)deserializer.Deserialize(reader);
}
}
Finally to my problem. When I deserialize, it is being deserialized to the base class IntroductionAction and not to the derived classes.
These IntroductionAction classes are just part of a much larger object that I am serializing/deserializing. I have tried making the base class abstract since it contains no functionality but I get an error on deserialization saying
The specified type is abstract: name='IntroductionAction'
Despite my XmlIncludes it seems unable to find the derived classes.
I have tried adding the types to the serializer but that didn't work.
Any help is much appreciated.
Edit:
This is what I mean by adding the types to the serializer
XmlSerializer deserializer = new XmlSerializer(obj.GetType(), new Type [] { typeof(IntroductionActionComplex), typeof(IntroductionActionSimple) });
using (TextReader reader = new StreamReader(file1))
{
return (T)deserializer.Deserialize(reader);
}
Also my attempt at using XmlAttributeOverrides:
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes();
XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "IntroductionAction";
attr.Type = typeof(IntroductionActionComplex);
attrs.XmlElements.Add(attr);
attr.ElementName = "IntroductionAction";
attr.Type = typeof(IntroductionActionSimple);
attrs.XmlElements.Add(attr);
attrOverrides.Add(typeof(IntroductionAction), "IntroductionAction", attrs);
XmlSerializer deserializer = new XmlSerializer(obj.GetType(), attrOverrides);
using (TextReader reader = new StreamReader(file1))
{
return (T)deserializer.Deserialize(reader);
}
I think you are pretty close. Below is the full example of saving and loading the XML file based on derived class types. This will save the nodes as the derived type itself, so loading back in will keep the desired type, rather than convert back to the base type. You'll probably need to add exception handling, this was just a quick solution. I did not change your base IntroductionAction or the derived IntroductionActionComplex / IntroductionActionSimple classes.
public class RootNode
{
[XmlElement("IntroductionAction")]
public List<IntroductionAction> introductionActions { get; set; }
public RootNode()
{
introductionActions = new List<IntroductionAction>();
}
private static XmlAttributeOverrides GetXmlAttributeOverrides()
{
XmlAttributeOverrides xml_attr_overrides = new XmlAttributeOverrides();
XmlAttributes xml_attrs = new XmlAttributes();
xml_attrs.XmlElements.Add(new XmlElementAttribute(typeof(IntroductionActionComplex)));
xml_attrs.XmlElements.Add(new XmlElementAttribute(typeof(IntroductionActionSimple)));
xml_attr_overrides.Add(typeof(RootNode), "introductionActions", xml_attrs);
return xml_attr_overrides;
}
// Add exception handling
public static void SaveToFile(RootNode rootNode, string fileName)
{
using (MemoryStream mem_stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(rootNode.GetType(), RootNode.GetXmlAttributeOverrides());
serializer.Serialize(mem_stream, rootNode);
using (BinaryWriter output = new BinaryWriter(new FileStream(fileName, FileMode.Create)))
{
output.Write(mem_stream.ToArray());
}
}
}
// Add exception handling
public static RootNode LoadFromFile(string fileName)
{
if (File.Exists(fileName))
{
using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (TextReader reader = new StreamReader(file))
{
XmlSerializer serializer = new XmlSerializer(typeof(RootNode), RootNode.GetXmlAttributeOverrides());
return (RootNode)serializer.Deserialize(reader);
}
}
}
return null;
}
}
Test program:
internal class Program
{
private static void Main(string[] args)
{
RootNode obj = new RootNode();
obj.introductionActions.Add(new IntroductionActionComplex() { question = "qTest", answerStrings = { "aTest1", "aTest2" }, name = "aName1" });
obj.introductionActions.Add(new IntroductionActionSimple() { name = "aName2", Value = "aValue" });
RootNode.SaveToFile(obj, "Test.xml");
RootNode obj2 = RootNode.LoadFromFile("Test.xml");
}
}

Is there any design pattern available when using XML serialization in C#

I am using Serialization and De serialization for parsing and creating XML content. Is there any Design pattern available particularly for this serialization in C# , so that the design pattern can be used for efficient handling of serialization ?
Depending on the use case, 'DataContract' may be a good way for you to serialize/deserialize your objects -
https://msdn.microsoft.com/en-us/library/vstudio/system.runtime.serialization.datacontractattribute%28v=vs.100%29.aspx
namespace DataContractAttributeExample
{
// Set the Name and Namespace properties to new values.
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
// To implement the IExtensibleDataObject interface, you must also
// implement the ExtensionData property.
private ExtensionDataObject extensionDataObjectValue;
public ExtensionDataObject ExtensionData
{
get
{
return extensionDataObjectValue;
}
set
{
extensionDataObjectValue = value;
}
}
[DataMember(Name = "CustName")]
internal string Name;
[DataMember(Name = "CustID")]
internal int ID;
public Person(string newName, int newID)
{
Name = newName;
ID = newID;
}
}
class Test
{
public static void Main()
{
try
{
WriteObject("DataContractExample.xml");
ReadObject("DataContractExample.xml");
Console.WriteLine("Press Enter to end");
Console.ReadLine();
}
catch (SerializationException se)
{
Console.WriteLine
("The serialization operation failed. Reason: {0}",
se.Message);
Console.WriteLine(se.Data);
Console.ReadLine();
}
}
public static void WriteObject(string path)
{
// Create a new instance of the Person class and
// serialize it to an XML file.
Person p1 = new Person("Mary", 1);
// Create a new instance of a StreamWriter
// to read and write the data.
FileStream fs = new FileStream(path,
FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
DataContractSerializer ser =
new DataContractSerializer(typeof(Person));
ser.WriteObject(writer, p1);
Console.WriteLine("Finished writing object.");
writer.Close();
fs.Close();
}
public static void ReadObject(string path)
{
// Deserialize an instance of the Person class
// from an XML file. First create an instance of the
// XmlDictionaryReader.
FileStream fs = new FileStream(path, FileMode.OpenOrCreate);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
// Create the DataContractSerializer instance.
DataContractSerializer ser =
new DataContractSerializer(typeof(Person));
// Deserialize the data and read it from the instance.
Person newPerson = (Person)ser.ReadObject(reader);
Console.WriteLine("Reading this object:");
Console.WriteLine(String.Format("{0}, ID: {1}",
newPerson.Name, newPerson.ID));
fs.Close();
}
}
}

How to change the DataContractSerializer text encoding?

When writing to a stream the DataContractSerializer uses an encoding different from Unicode-16. If I could force it to write/read Unicode-16 I could store it in a SQL CE's binary column and read it with SELECT CONVERT(nchar(1000), columnName). But the way it is, I can't read it, except programatically.
Can I change the encoding used by System.Runtime.Serialization.DataContractSerializer?
The DataContractSerializer's WriteObject method has overloads which write to a Stream or to a XmlWriter (and XmlDictionaryWriter). The Stream overload will default to UTF-8, so you'll need to use another one. Using a XML Writer instance which writes the XML in UTF-16 do what you needs, so you can either do what #Phil suggested, or you can use the writer returned by XmlDictionaryWriter.CreateTextWriter for which you pass an Encoding.Unicode as a parameter.
public class StackOverflow_10089682
{
[DataContract(Name = "Person", Namespace = "http://my.namespace")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
public static void Test()
{
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.Unicode);
DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
Person instance = new Person { Name = "John Doe", Age = 33 };
dcs.WriteObject(writer, instance);
writer.Flush(); // Don't forget to Flush the writer here
Console.WriteLine("Decoding using UTF-16: {0}", Encoding.Unicode.GetString(ms.ToArray()));
}
}
Have you tried using XmlWriterSettings? Something like
var s = new DataContractSerializer (typeof(Thing));
using(var wr = XmlTextWriter.Create(
#"test.xml", new XmlWriterSettings{Encoding=Encoding.UTF32}))
{
s.WriteObject(wr, new Thing{Foo="bar"});
}
public class Thing
{
public string Foo { get; set; }
}
Specify the Encoding you require.

Querying serialized object file

Is there anyway to achieve that without loading the whole file into memory? If so, what do you suggest me to do?
Class implementation:
[Serializable()]
public class Car
{
public string Brand { get; set; }
public string Model { get; set; }
}
[Serializable()]
public class CarCollection : List<Car>
{
}
Serialization to file:
CarCollection cars = new CarCollection
{
new Cars{ Brand = "BMW", Model = "7.20" },
new Cars{ Brand = "Mercedes", Model = "CLK" }
};
using (Stream stream = File.Open("data", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream, cars);
}
If you serialize to XML you can use a SAX parser (XmlReader class), which will read from a stream seqentially.
To deserialize the collection one object at a time, you also need to serialize it one at a time.
Simplest way is to define your own generic class:
public static class StreamSerializer
{
public static void Serialize<T>(IList<T> list, string filename)
{
using (Stream stream = File.Open(filename, FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
// seralize each object separately
foreach (var item in list)
bin.Serialize(stream, item);
}
}
public static IEnumerable<T> Deserialize<T>(string filename)
{
using (Stream stream = File.Open(filename, FileMode.Open))
{
BinaryFormatter bin = new BinaryFormatter();
// deserialize each object separately, and
// return them one at a time
while (stream.Position < stream.Length)
yield return (T)bin.Deserialize(stream);
}
}
}
Then you can simply write:
CarsCollection cars = new CarsCollection
{
new Cars{ Brand = "BMW", Model = "7.20" },
new Cars{ Brand = "Mercedes", Model = "CLK" }
};
// note that you cannot serialize the entire list if
// you want to query without loading - it must be symmetrical
StreamSerializer.Serialize(cars, "data.bin");
// the following expression iterates through objects, processing one
// at a time. "First" method is a good example because it
// breaks early.
var bmw = StreamSerializer
.Deserialize<Cars>("data.bin")
.First(c => c.Brand == "BMW");
A slightly more complex case might be if your CarsCollection belongs to a different class. In that case, you will need to implement ISerializable, but the principle is similar.
On a side note, usual convention is not to name entities in plural (i.e. Cars should be named Car).
Generally you can use some sort of reader (StreamReader, BinaryReader, ...) together with BufferedStream.

Categories