Reputation: 5955
I am having an issue serializing a c# class to an XML file that has a base class... here is a simple example:
namespace Domain
{
[Serializable]
public class ClassA
{
public virtual int MyProperty
{
get; set;
}
}
}
namespace Derived
{
public class ClassA : Domain.ClassA
{
public override int MyProperty
{
get { return 1; } set { /* Do Nothing */ }
}
}
}
When I attempt to serialize an instance of Derived.ClassA, I receive the following exception:
InvalidOperationException(Types 'Domain.ClassA' and 'Derived.ClassA' both use the XML type name 'ClassA', from the namespace ". Use XML attributes to specify a unique XML name and/or namespace for the type.)
The problem is that I want to create a single base class that simply defines the structure of the XML file, and then allow anyone else to derive from that class to insert business rules, but that the formatting will come through from the base.
Is this possible, and if so, how do I attribute the base class to allow this?
Upvotes: 4
Views: 2787
Reputation: 51
This did the trick for us.
[XmlType(TypeName = "DerivedClassA")]
Upvotes: 5
Reputation:
This is just informational as it doesn't provide a specific answer, but here's a good article from the July 2003 MSDN issue discussing these name collisions. "XML Namespace Collisions, XmlNodeList and Deserialization, and More".
Upvotes: 0
Reputation: 437644
I had this same problem, but with an added restriction: no access to the source for either the Domain or the Derived classes. A Derived object inherited from a Domain object, and, because the classes had the same unqualified names, XmlSerializer would fail to serialize an object of the Derived class.
This could have probably been solved with brute force (modifying the IL for one of the classes to add the appropriate attributes), but I wanted something "cleaner". Here's what worked:
var attrs = new XmlAttributes();
attrs.XmlType = new XmlTypeAttribute("anythingButClassA");
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Domain.ClassA), attrs);
var serializer = new XmlSerializer(typeof(Derived.ClassA), overrides);
serializer.Serialize(Console.Out, new Derived.ClassA());
Even if you are the author of the Domain and/or Derived classes, this way you do not need to rename them.
Upvotes: 0
Reputation: 1889
Set a base namespace for your domain and a set custom element name for the derived class:
namespace Domain
{
[Serializable]
[XmlRoot(Namespace = "http://mynamespace/domain/2009/")]
public class ClassA
{
[XmlIgnore]
public virtual int MyProperty { get; set; }
}
}
namespace Derived
{
[Serializable]
[XmlRoot(ElementName = "DerivedClassA")]
public class ClassA : Domain.ClassA
{
public override int MyProperty
{
get
{
return 1;
}
set
{
base.MyProperty = value;
}
}
}
}
Upvotes: 0
Reputation: 48623
If you're free to rename the derived classes to something different than their base class, you can use XmlAttributeOverrides
to do this:
// For ClassB, which derives from ClassA
XmlAttributes attributes = new XmlAttributes();
attributes.XmlRoot = new XmlRootAttribute("ClassA");
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(ClassB), attributes);
XmlSerializer serializer = new XmlSerializer(typeof(ClassB), overrides);
This will serialize to <ClassA> ... </ClassA>
and can serialize from that into ClassB
instances.
As far as I can tell, there's no way to do this when the base and derived classes have the same name (outside of taking full control of the serialization process via Data Contract Surrogates or some other overkill method).
Upvotes: 2
Reputation: 755128
I think your derived class also needs to be marked with the [Serializable]
attribute to be serializable / deserializable.
Besides the serializable attribute, you also need to make one of the two have a different XML name - as the error says, in "XML world", they're both called "ClassA". One of them must be named differently, using the XmlElement
attribute (assuming you're using the XmlSerializer - right?)
namespace Domain
{
[Serializable]
[XmlElement(ElementName="ClassABase")]
public class ClassA
{
public virtual int MyProperty
{
get; set;
}
}
}
namespace Derived
{
[Serializable]
public class ClassA : Domain.ClassA
{
public override int MyProperty
{
get { return 1; } set { /* Do Nothing */ }
}
}
}
Marc
Upvotes: 0