Recently I have been messing around with figuring out how to serialize some data back & forth between two applications. The code in particular leveraged several interfaces, classes with readonly properties, & generics. This caused a couple problems with serialization which I thought might be useful to share for others that run into a similar problem.
Take for instance the following code:
If you run this through & try to serialize & then deserialize you will run into a problem just serializing this. The first error you will hit is:
System.InvalidOperationException was unhandled
Message=There was an error generating the XML document.
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o)
Message=MyNamespace.MyClass`1[System.String] cannot be serialized because it does not have a parameterless constructor.
Xml Serialization requires you to have a parameterless constructor, so you can either add one, or switch to using WCF DataContract Serialization. This is as simple as changing the attribute on your Classes from [Serializable] to [DataContract] and then updating your Serialize & Deserialize methods. This will allow you to successfully serialize & deserialize the objects without an exception.
Serializing Read-only Properties
Next you will his the Debug.Assert which was a check to ensure we are serializing the read-only property was correctly being populated & in this case it is not. The reason being is that XmlSerialization will automatically attempt to serialize all public properties, but WCD DataSerialization does not automatically attempt to serialize any properties. So you need to decorate any item you want serialized with a [DataMember] tag. This is great because now for readonly properties you can just mark the underlying private data as the DataMember.
The next problem you will hit is a SerializationException about not being able to Serialize the generic MyClassOfString, aka MyClass<string>
System.Runtime.Serialization.SerializationException was unhandled
Message=Type 'MyNamespace.MyClass`1[[System.String, mscorlib, Version=126.96.36.199, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' with data contract name 'MyClassOfstring:http://schemas.datacontract.org/2004/07/MyNamespace' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
To resolve this you will need to use the KnownType attribute & there are a couple options:
- Generically implement serialization
- Explicitly list the known types
Generic serialization is nice as the serialize since you don’t have to decorate your code with the various possible classes used for your generic. To implement it you add the KnownTypes attribute to your class with a string of the method name to call.
If you do the above then you will wind up with an exception during deserialization now since Class1 does not how to deserialize MyClassOfString. As the deserializer you will need to explictly state what types you expect. In this case you can do it with a single attribute on the top.
At this point the class will successfully serialize & deserialize fully. Due to the nature of my example, the step of using the GetTypes method is not required since I am doing both serialization & deserialization in Class1, but it is useful when that is not the case.