How can I force .NET's XmlSerializer to add an xsi:type="FooClass" to a member/node of type FooClass
?
The scenario is a currently-released app, where in v.1 :
- FooClass inherits FooBaseClass
- FooPropertyA is on FooBaseClass
- FooPropertyB is on FooClass
- FooBaseClass is decorated with [XmlInclude(typeof(FooClass))]
- BazClass has member Foo of type FooBaseClass
- All sets of Baz.Foo are to a FooClass instance
- All usages of Baz.Foo expect FooPropertyB (and so a FooClass instance vs FooBaseClass)
The goal: Remove FooBaseClass entirely, pushing FooBaseClass's members up into FooClass, while maintaining backward serialization compatibility
The problem: Then I lose the xsi:type="FooClass" attribute on the Baz.Foo serialization.
In other words the XmlSerializer.Serialize output of
public class BazClass
{
public BazClass()
{
Foo = new FooClass { A = 5, B = "Hello" };
}
public FooClass Foo { get; set; }
}
public class FooClass
{
public int FooPropertyA { get; set; }
public string FooPropertyB { get; set; }
}
needs to be
<Baz>
<Foo xsi:type="FooClass">
<FooPropertyA>Hello</FooPropertyA>
<FooPropertyB>5</FooPropertyB>
</Foo>
</Baz>
Removing FooBasClass is easy, but then XmlSerializer no longer places xsi:type="FooClass" on Baz/Foo, and so v.1 XmlSerializer.Deserialize instantiates a FooBaseClass instance, not setting FooPropertyB, and assigns it to the Foo property of the parent Baz instance. Thus, any code which checks whether Baz.Foo is FooClass, or casts directly, fails.
The xsi:type attribute was placed automatically in the v.1 code which was
public class BazClass
{
public BazClass()
{
Foo = new FooClass { A = 5, B = "Hello" };
}
public FooBaseClass Foo { get; set; }
}
public class FooClass : FooBaseClass
{
public string FooPropertyB { get; set; }
}
[XmlInclude(typeof(FooClass))]
public class FooBaseClass
{
public int FooPropertyA { get; set; }
}
I think the short answer is that you can't - at least not without implementing I(Xml)Serializable or writing custom serialization code. However, I'm open to good suggestions. Meanwhile I have implemented a workaround hack below, and am hoping for something more elegant, or that at least allows me somehow to remove FooBaseClass entirely.
BazClass
{
[XmlElement("Foo")]
public FooBaseClass XmlFoo { get { return Foo; } set { Foo = (StartPicture)value; } }
[XmlIgnore]
public FooClass Foo { get; set; }
}
FooClass : FooBaseClass
{
public int FooPropertyB { get; set; }
public string FooPropertyA { get; set; }
}
[XmlInclude("FooClass")]
FooBaseClass
{
}
See Question&Answers more detail:
os