views:

621

answers:

6

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?

A: 

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

marc_s
Tried that, but it didn't help. Anything else?
Nick
+2  A: 

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).

Jeff Sternal
The solution that eventually worked was similar to this, but not quite as complicated. You are correct in that the .NET class names have to be different... unfortunately, .NET namespaces are not used for whatever reason. So the simplest was to have a ClassABase for the simple version, and attach the XmlRoot attribute to it of ClassA, and then have the derived class called ClassA. They both serialized identically then.The overrides as part of the serializer where not needed.
Nick
Excellent! But will that work for ClassB that derives from ClassABase? Or do you only need one subclass?
Jeff Sternal
A: 

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;
            }
        }
    }
}
Chris Martin
+3  A: 

This did the trick for us.

[XmlType(TypeName = "DerivedClassA")] 
Joris Veltkamp
A: 

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.

Jon
A: 

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".

Billy