How to serialize an interface member

I have a class that has a property that is defined as an interface. Users of my class can assign to this property any implementation of the class that implements the interface. I want to be able to load this class state from a text file on disk. Users should be able to manually modify the xml file to control the operation of the application.

If I try to serialize my class, it tells me that I can not serialize the interface. I understand that the serializer does not know about the structure of the property class, only knowing that it implements the interface.

I would expect it to call GetType on the member and reflect in the structure of the actual class. Is there any way to achieve this? Is there any other way to fulfill my requirement?

Edit : Clarification of my intentions: Let's say I have this class:

class Car { IEngine engine } class ElectricEngine : IEngine { int batteryPrecentageLeft; } class InternalCombustionEngine : IEngine { int gasLitersLeft; } 

and the class user has a class with

 Car myCar = new Car(); myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70}; 

When I serialize the myCar class, I expect the xml to look like this:

 <Car> <Engine> <ElectricEngine> <batteryPrecentageLeft>70</batteryPrecentageLeft> </ElectricEngine> <Engine> </Car> 
+8
c # xml-serialization
source share
4 answers

Based on the @Jens solution, I created a serializer that does what I need. Thanks, Jen. Here is the code:

 public class RuntimeXmlSerializerAttribute : XmlIgnoreAttribute { } public class RuntimeXmlSerializer { private Type m_type; private XmlSerializer m_regularXmlSerializer; private const string k_FullClassNameAttributeName = "FullAssemblyQualifiedTypeName"; public RuntimeXmlSerializer(Type i_subjectType) { this.m_type = i_subjectType; this.m_regularXmlSerializer = new XmlSerializer(this.m_type); } public void Serialize(object i_objectToSerialize, Stream i_streamToSerializeTo) { StringWriter sw = new StringWriter(); this.m_regularXmlSerializer.Serialize(sw, i_objectToSerialize); XDocument objectXml = XDocument.Parse(sw.ToString()); sw.Dispose(); SerializeExtra(i_objectToSerialize,objectXml); string res = objectXml.ToString(); byte[] bytesToWrite = Encoding.UTF8.GetBytes(res); i_streamToSerializeTo.Write(bytesToWrite, 0, bytesToWrite.Length); } public object Deserialize(Stream i_streamToSerializeFrom) { string xmlContents = new StreamReader(i_streamToSerializeFrom).ReadToEnd(); StringReader sr; sr = new StringReader(xmlContents); object res = this.m_regularXmlSerializer.Deserialize(sr); sr.Dispose(); sr = new StringReader(xmlContents); XDocument doc = XDocument.Load(sr); sr.Dispose(); deserializeExtra(res, doc); return res; } private void deserializeExtra(object i_desirializedObject, XDocument i_xmlToDeserializeFrom) { IEnumerable propertiesToDeserialize = i_desirializedObject.GetType() .GetProperties().Where(p => p.GetCustomAttributes(true) .FirstOrDefault(a => a.GetType() == typeof(RuntimeXmlSerializerAttribute)) != null); foreach (PropertyInfo prop in propertiesToDeserialize) { XElement propertyXml = i_xmlToDeserializeFrom.Descendants().FirstOrDefault(e => e.Name == prop.Name); if (propertyXml == null) continue; XElement propertyValueXml = propertyXml.Descendants().FirstOrDefault(); Type type = Type.GetType(propertyValueXml.Attribute(k_FullClassNameAttributeName).Value.ToString()); XmlSerializer srl = new XmlSerializer(type); object deserializedObject = srl.Deserialize(propertyValueXml.CreateReader()); prop.SetValue(i_desirializedObject, deserializedObject, null); } } private void SerializeExtra(object objectToSerialize, XDocument xmlToSerializeTo) { IEnumerable propertiesToSerialize = objectToSerialize.GetType().GetProperties().Where(p => p.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(RuntimeXmlSerializerAttribute)) != null); foreach (PropertyInfo prop in propertiesToSerialize) { XElement serializedProperty = new XElement(prop.Name); serializedProperty.AddFirst(serializeObjectAtRuntime(prop.GetValue(objectToSerialize, null))); xmlToSerializeTo.Descendants().First().Add(serializedProperty); //TODO } } private XElement serializeObjectAtRuntime(object i_objectToSerialize) { Type t = i_objectToSerialize.GetType(); XmlSerializer srl = new XmlSerializer(t); StringWriter sw = new StringWriter(); srl.Serialize(sw, i_objectToSerialize); XElement res = XElement.Parse(sw.ToString()); sw.Dispose(); XAttribute fullClassNameAttribute = new XAttribute(k_FullClassNameAttributeName, t.AssemblyQualifiedName); res.Add(fullClassNameAttribute); return res; } } 
+2
source share

You can mark a property as do-not-include.

However, there is a deeper problem: serialization can only capture a simple β€œstate”, not behavior. Your class does not have a serializable type. What value do you expect from a property after deserialization? null is the only option.

The correct workaround would be to think about what you really need to save and use the DTO for this part.


The following model can be serialized:

 public class BaseEngine { } [XmlInclude(typeof(InternalCombustionEngine))] [XmlInclude(typeof(ElectricEngine))] public class Car { public BaseEngine Engine { get; set; } } 
+7
source share

Perhaps you could use a base class instead of an interface and serialize it.

Update

I realized that using a base class is not really an option for you.

The best solution would probably be a workaround with the DTO, as stated by Henk Holterman.

But if you really need a solution for your question, I think you will need to create your own serializer, but I would not recommend this because you would get a lot of errors for sorting.

Here is an example of a custom serializer, keep in mind that this example will be necessary for some work to be complete in a real application.

To do this, at least add two things that can work not only as an example:

  • Exception Handling
  • Listing or converting the value of an xml element to correct the type in the string anyThingProperty.SetValue(obj, propertyElement.Value, null);
 [TestClass] public class SerializableInterfaceTest { [TestMethod] public void TestMethod1() { string serialize = AnyThingSerializer.Serialize( new SerializableClass {Name = "test", Description = "test1", AnyThing = new Animal {Name = "test", Color = "test1"}}); Console.WriteLine(serialize); object obj = AnyThingSerializer.Deserialize(serialize); } } public sealed class SerializableClass { public string Name { get; set; } public string Description { get; set; } [AnyThingSerializer] public object AnyThing { get; set; } } public static class AnyThingSerializer { public static string Serialize(object obj) { Type type = obj.GetType(); var stringBuilder = new StringBuilder(); var serializer = new XmlSerializer(type); serializer.Serialize(new StringWriter(stringBuilder), obj); XDocument doc = XDocument.Load(new StringReader(stringBuilder.ToString())); foreach (XElement xElement in SerializeAnyThing(obj)) { doc.Descendants().First().Add(xElement); } return doc.ToString(); } public static object Deserialize(string xml) { var serializer = new XmlSerializer(typeof (T)); object obj = serializer.Deserialize(new StringReader(xml)); XDocument doc = XDocument.Load(new StringReader(xml)); DeserializeAnyThing(obj, doc.Descendants().OfType().First()); return obj; } private static void DeserializeAnyThing(object obj, XElement element) { IEnumerable anyThingProperties = obj.GetType() .GetProperties().Where(p => p.GetCustomAttributes(true) .FirstOrDefault(a => a.GetType() == typeof (AnyThingSerializerAttribute)) != null); foreach (PropertyInfo anyThingProperty in anyThingProperties) { XElement propertyElement = element.Descendants().FirstOrDefault(e => e.Name == anyThingProperty.Name && e.Attribute("type") != null); if (propertyElement == null) continue; Type type = Type.GetType(propertyElement.Attribute("type").Value); if (IsSimpleType(type)) { anyThingProperty.SetValue(obj, propertyElement.Value, null); } else { object childObject = Activator.CreateInstance(type); DeserializeAnyThing(childObject, propertyElement); anyThingProperty.SetValue(obj, childObject, null); } } } private static List SerializeAnyThing(object obj) { var doc = new List(); IEnumerable anyThingProperties = obj.GetType().GetProperties().Where(p => p.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof (AnyThingSerializerAttribute)) != null); foreach (PropertyInfo anyThingProperty in anyThingProperties) { doc.Add(CreateXml(anyThingProperty.Name, anyThingProperty.GetValue(obj, null))); } return doc; } private static XElement CreateXml(string name, object obj) { var xElement = new XElement(name); Type type = obj.GetType(); xElement.Add(new XAttribute("type", type.AssemblyQualifiedName)); foreach (PropertyInfo propertyInfo in type.GetProperties()) { object value = propertyInfo.GetValue(obj, null); if (IsSimpleType(propertyInfo.PropertyType)) { xElement.Add(new XElement(propertyInfo.Name, value.ToString())); } else { xElement.Add(CreateXml(propertyInfo.Name, value)); } } return xElement; } private static bool IsSimpleType(Type type) { return type.IsPrimitive || type == typeof (string); } } public class AnyThingSerializerAttribute : XmlIgnoreAttribute { } 
+5
source share

You can use ExtendedXmlSerializer . If you have classes:

 public interface IEngine { string Name {get;set;} } public class Car { public IEngine Engine {get;set;} } public class ElectricEngine : IEngine { public string Name {get;set;} public int batteryPrecentageLeft {get;set;} } public class InternalCombustionEngine : IEngine { public string Name {get;set;} public int gasLitersLeft {get;set;} } 

and create an instance of this class:

 Car myCar = new Car(); myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70, Name = "turbo diesel"}; 

You can serialize this object using ExtendedXmlSerializer:

 ExtendedXmlSerializer serializer = new ExtendedXmlSerializer(); var xml = serializer.Serialize(myCar); 

The xml result will look like this:

 <?xml version="1.0" encoding="utf-8"?> <Car type="Program+Car"> <Engine type="Program+ElectricEngine"> <Name>turbo diesel</Name> <batteryPrecentageLeft>70</batteryPrecentageLeft> </Engine> </Car> 

You can install ExtendedXmlSerializer from nuget or run the following command:

 Install-Package ExtendedXmlSerializer 

Here is an online example

+1
source share

All Articles