Last week I ran into the following exception when serializing an object into xml:
System.InvalidOperationException: There was an error generating the XML document. —> System.InvalidOperationException: The type <Type> was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
The business object I was trying to serialize had a similar construction like these classes:
public class FooParent
{
public Foo MyFoo { get; set; }
public List<Foo> MyFoos { get; set; }
}
public class Foo
{
public int X { get; set; }
public int Y { get; set; }
}
However the Foo members of the FooParent where not set with Foo objects, but with SqlFoo objects derived from Foo and with a class definition in a different assembly:
public class SqlFoo : Foo
{
public SqlFoo() : this(0) {}
public SqlFoo(int id)
{
this.ID = id;
}
public int ID { get; set; }
}
So, the next test example will throw the InvalidOperationException:
FooParent target = new FooParent();
target.MyFoo = new SqlFoo(1);
target.MyFoos = new List<Foo>(new SqlFoo[] { new SqlFoo(2) });
XmlSerializer serializer = new XmlSerializer(typeof(FooParent));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, target);
After consulting my dear friend Dr. Google, I found a nice solution from Simon Hewitt. I really liked the usage of the implicit casting operators! But I didn’t like implementing the IXmlSerializable interface. There is too much thinking involved about XmlAttributeOverrides and XmlReader, and I wishes there would be a more intuitively solution.
Luckily I also had an Epiphany of my own! I tried this Foo wrapper, which continues the idea of using the casting operators:
public class XmlFooWrapper
{
private Foo model;
public XmlFooWrapper() : this(new Foo()) {}
public XmlFooWrapper(Foo model)
{
this.model = model;
}
public static implicit operator Foo(XmlFooWrapper wrapper)
{
return wrapper != null ? wrapper.model : null;
}
public static implicit operator XmlFooWrapper(Foo model)
{
return model!= null ? new XmlFooWrapper(model) : null;
}
public int X
{
get { return model.X; }
set { model.X = value; }
}
public int Y
{
get { return model.Y; }
set { model.Y = value; }
}
}
The FooParent was altered by adding the [XmlElement(Type = typeof(XmlFooWrapper))] attribute to the MyFoo member. The List<> member was decorated with the [XmlArrayItem(ElementName="Foo", Type=typeof(XmlFooWrapper))] attribute:
public class FooParent
{
[XmlElement(Type = typeof(XmlFooWrapper))]
public Foo MyFoo { get; set; }
[XmlArrayItem(ElementName = "Foo", Type = typeof(XmlFooWrapper))]
public List<Foo> MyFoos { get; set; }
}
It worked! Running the test example again results in the following xml:
<FooParent>
<MyFoo>
<X>0</X>
<Y>0</Y>
</MyFoo>
<MyFoos>
<Foo>
<X>0</X>
<Y>0</Y>
</Foo>
</MyFoos>
</FooParent>
Very useful, thanks