XML serialization of an interface property

I would like XML to serialize an object that has (among others) a property of type IModelObject (which is an interface).

public class Example { public IModelObject Model { get; set; } } 

When I try to serialize an object of this class, I get the following error:
"It is not possible to serialize an Example.Model element of type Example because it is an interface."

I understand that the problem is that the interface cannot be serialized. However, the specific type of model object is not known until run time.

Replacing the IModelObject interface with an abstract or concrete type and using inheritance with XMLInclude is possible, but seems like an ugly workaround.

Any suggestions?

+68
c # serialization
Aug 26 '09 at 10:54
source share
8 answers

This is simply an integral limitation of declarative serialization, where type information is not embedded in the output.

When trying to convert <Flibble Foo="10" /> back to

 public class Flibble { public object Foo { get; set; } } 

How does the serializer know if it should be int, string, double (or something else) ...

You have a few options to make this work, but if you really don't know before the runtime, the easiest way to do this is most likely to use XmlAttributeOverrides .

Unfortunately, this will only work with base classes, not with interfaces. The best you can do is ignore a property that is not enough for your needs.

If you really need to stay with the interfaces, you have three real options:

Hide it and handle it in another property

An ugly, unpleasant boiler plate and many repetitions, but most consumers of this class will not have to deal with the problem:

 [XmlIgnore()] public object Foo { get; set; } [XmlElement("Foo")] [EditorVisibile(EditorVisibility.Advanced)] public string FooSerialized { get { /* code here to convert any type in Foo to string */ } set { /* code to parse out serialized value and make Foo an instance of the proper type*/ } } 

This is likely to become a nightmare for maintenance ...

Implement IXmlSerializable

Like the first option is that you are in full control of things, but

  • Pros
    • You have no unpleasant "fake" properties that hang around.
    • you can interact directly with the xml structure by adding flexibility / version control
  • Cons
    • you may need to reuse the wheel for all other class properties

The issues of duplication of effort are similar to the first.

Change property to use packaging type

 public sealed class XmlAnything<T> : IXmlSerializable { public XmlAnything() {} public XmlAnything(T t) { this.Value = t;} public T Value {get; set;} public void WriteXml (XmlWriter writer) { if (Value == null) { writer.WriteAttributeString("type", "null"); return; } Type type = this.Value.GetType(); XmlSerializer serializer = new XmlSerializer(type); writer.WriteAttributeString("type", type.AssemblyQualifiedName); serializer.Serialize(writer, this.Value); } public void ReadXml(XmlReader reader) { if(!reader.HasAttributes) throw new FormatException("expected a type attribute!"); string type = reader.GetAttribute("type"); reader.Read(); // consume the value if (type == "null") return;// leave T at default value XmlSerializer serializer = new XmlSerializer(Type.GetType(type)); this.Value = (T)serializer.Deserialize(reader); reader.ReadEndElement(); } public XmlSchema GetSchema() { return(null); } } 

Using this will include something like (in project P):

 public namespace P { public interface IFoo {} public class RealFoo : IFoo { public int X; } public class OtherFoo : IFoo { public double X; } public class Flibble { public XmlAnything<IFoo> Foo; } public static void Main(string[] args) { var x = new Flibble(); x.Foo = new XmlAnything<IFoo>(new RealFoo()); var s = new XmlSerializer(typeof(Flibble)); var sw = new StringWriter(); s.Serialize(sw, x); Console.WriteLine(sw); } } 

which gives you:

 <?xml version="1.0" encoding="utf-16"?> <MainClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <RealFoo> <X>0</X> </RealFoo> </Foo> </MainClass> 

This is obviously more cumbersome for users of this class, although it avoids a large number of boiler plates.

A happy environment can combine the idea of โ€‹โ€‹XmlAnything into the โ€œsupportโ€ property of the first technique. Thus, most of the grumbling is done for you, but consumers of this class do not suffer from confusion with introspection.

+101
Sep 03 '09 at 22:46
source share

The solution to this is to use reflection with the DataContractSerializer. You do not even need to mark your class [DataContract] or [DataMember]. It will serialize any object, regardless of whether it has interface type properties (including dictionaries) in xml. Here is a simple extension method that will serialize any object in XML, even if it has interfaces (note that you can configure it the same way as it does recursively).

  public static XElement ToXML(this object o) { Type t = o.GetType(); Type[] extraTypes = t.GetProperties() .Where(p => p.PropertyType.IsInterface) .Select(p => p.GetValue(o, null).GetType()) .ToArray(); DataContractSerializer serializer = new DataContractSerializer(t, extraTypes); StringWriter sw = new StringWriter(); XmlTextWriter xw = new XmlTextWriter(sw); serializer.WriteObject(xw, o); return XElement.Parse(sw.ToString()); } 

what the LINQ expression does is enumerate each property, return each property that is an interface, get the value of that property (the base object), get the type of this particular object, put it into an array, and add it to the list of sorters of known types.

Now the serializer knows how about the types that it serializes so that it can do its job.

+38
Feb 16 2018-12-12T00:
source share

Replacing the IModelObject interface with an abstract or concrete type and using inheritance with XMLInclude is possible, but seems like an ugly workaround.

If you can use an abstract base, I would recommend this route. It will still be cleaner than using manual serialization. The only problem I see with the abstract base is that you still need a specific type? At least that's how I used it in the past, something like:

 public abstract class IHaveSomething { public abstract string Something { get; set; } } public class MySomething : IHaveSomething { string _sometext; public override string Something { get { return _sometext; } set { _sometext = value; } } } [XmlRoot("abc")] public class seriaized { [XmlElement("item", typeof(MySomething))] public IHaveSomething data; } 
+3
03 Sep '09 at 17:04
source share

You can use ExtendedXmlSerializer . This serializer supports serializing interface properties without any tricks.

 var serializer = new ConfigurationContainer().UseOptimizedNamespaces().Create(); var obj = new Example { Model = new Model { Name = "name" } }; var xml = serializer.Serialize(obj); 

Your xml will look like this:

 <?xml version="1.0" encoding="utf-8"?> <Example xmlns:exs="https://extendedxmlserializer.imtqy.com/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Simple;assembly=ExtendedXmlSerializer.Samples"> <Model exs:type="Model"> <Name>name</Name> </Model> </Example> 

ExtendedXmlSerializer supports .net 4.5 and .net Core.

+3
Sep 22 '16 at 8:10
source share

Unfortunately, there is no simple answer, because the serializer does not know what to serialize for the interface. I found a more complete explanation of how to get around this on MSDN

+2
03 Sep '09 at 13:41
source share

If you know that your interface developers have a simple hack way that you can use to get the type of interface to serialize without writing parsing code:

 public interface IInterface {} public class KnownImplementor01 : IInterface {} public class KnownImplementor02 : IInterface {} public class KnownImplementor03 : IInterface {} public class ToSerialize { [XmlIgnore] public IInterface InterfaceProperty { get; set; } [XmlArray("interface")] [XmlArrayItem("ofTypeKnownImplementor01", typeof(KnownImplementor01)] [XmlArrayItem("ofTypeKnownImplementor02", typeof(KnownImplementor02)] [XmlArrayItem("ofTypeKnownImplementor03", typeof(KnownImplementor03)] public object[] InterfacePropertySerialization { get { return new[] { InterfaceProperty } } set { InterfaceProperty = (IInterface)value.Single(); } } } 

The resulting xml should look something like this:

  <interface><ofTypeKnownImplementor01><!-- etc... --> 
+1
Mar 28 '16 at 16:46
source share

in my project, I have
List <IFormatStyle> FormatStyleTemplates;
containing different types.

Then I use the "XmlAnything" solution at the top to serialize this list of different types. The generated xml is beautiful.

  [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] [XmlArray("FormatStyleTemplates")] [XmlArrayItem("FormatStyle")] public XmlAnything<IFormatStyle>[] FormatStyleTemplatesXML { get { return FormatStyleTemplates.Select(t => new XmlAnything<IFormatStyle>(t)).ToArray(); } set { // read the values back into some new object or whatever m_FormatStyleTemplates = new FormatStyleProvider(null, true); value.ForEach(t => m_FormatStyleTemplates.Add(t.Value)); } } 
0
Dec 19 '14 at 2:02
source share

Unfortunately for me, I had a case where the class to be serialized had properties that had interfaces as properties, so I needed to recursively process each property. In addition, some interface properties were marked as [XmlIgnore], so I wanted to skip them. I took the ideas I found in this thread and added some things to it to make it recursive. Only deserialization code shown here:

 void main() { var serializer = GetDataContractSerializer<MyObjectWithCascadingInterfaces>(); using (FileStream stream = new FileStream(xmlPath, FileMode.Open)) { XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas()); var obj = (MyObjectWithCascadingInterfaces)serializer.ReadObject(reader); // your code here } } DataContractSerializer GetDataContractSerializer<T>() where T : new() { Type[] types = GetTypesForInterfaces<T>(); // Filter out duplicates Type[] result = types.ToList().Distinct().ToList().ToArray(); var obj = new T(); return new DataContractSerializer(obj.GetType(), types); } Type[] GetTypesForInterfaces<T>() where T : new() { return GetTypesForInterfaces(typeof(T)); } Type[] GetTypesForInterfaces(Type T) { Type[] result = new Type[0]; var obj = Activator.CreateInstance(T); // get the type for all interface properties that are not marked as "XmlIgnore" Type[] types = T.GetProperties() .Where(p => p.PropertyType.IsInterface && !p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false).Any()) .Select(p => p.GetValue(obj, null).GetType()) .ToArray(); result = result.ToList().Concat(types.ToList()).ToArray(); // do the same for each of the types identified foreach (Type t in types) { Type[] embeddedTypes = GetTypesForInterfaces(t); result = result.ToList().Concat(embeddedTypes.ToList()).ToArray(); } return result; } 
0
Nov 18 '17 at 5:33
source share



All Articles