Nick
Nick

Reputation: 5955

InvalidOperationException When XML Serializing Inherited Class

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

Answers (6)

Joris Veltkamp
Joris Veltkamp

Reputation: 51

This did the trick for us.

[XmlType(TypeName = "DerivedClassA")] 

Upvotes: 5

user292978
user292978

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

Jon
Jon

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

Chris Martin
Chris Martin

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

Jeff Sternal
Jeff Sternal

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

marc_s
marc_s

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

Related Questions