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 { } set { } }
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();
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.