5

I've got the following structure of abstract classes:

public abstract class Template
{
   // Some properties and methods defined
}

public abstract class Template<TTemplate> : Template where TTemplate : Template
{
 // No new properties defined, but methods overriden
}

I then use these template classes as part of a model:

public abstract class Model 
{
  public Template Template {get;set;}
  public Model(Template t) {Template = t;}
  // More properties and methods
}

public abstract class Model<TModel, TTemplate> : Model where TModel : Model where TTemplate : Template
{
  public new TTemplate template {get {return (TTemplate)base.Template;} set {base.Template = value;}}
  public Model(TTemplate t) : base(t) {}
  // Override some methods but no new properties
}

I then create concrete classes of my template and models and use them in my project. These concrete classes define additional properties beyond those specified in the abstract base classes. My problem comes when it's time to serialize the Model classes. I use reflection to find all inherited types of Model or Template, and pass them into the XmlSerializer so it can properly serialize my abstract classes. However, I get an exception

There was an error reflecting type **ConcreteModel**.

System.InvalidOperationException: There was an error reflecting property 'Template'. ---> System.InvalidOperationException: Member ModelOfConcreteModelConcreteTemplate.Template of type ConcreteTemplate hides base class member Model.Template of type Template. Use XmlElementAttribute or XmlAttributeAttribute to specify a new name.

I came across this post on google groups from 2003 which purports to give an answer, but I'm not sure how to implement that fix (or if it's even valid 13 years later). It does indicate that the error message is misleading as the solution proposed by the message does not work.

If I remove the 'set' accessor from the Model.Template and typed Model classes (and just set it via the constructor, for example), the class serializes just fine - albeit without the Template property. Is there a way to XML serialize classes which hide properties from a(n) (abstract) base class, without implementing IXmlSerializable on every single inherited class?

Muhammad zubair
  • 251
  • 1
  • 3
  • 13
mmathis
  • 1,610
  • 18
  • 29

1 Answers1

6

I came across this post by david.woodward, showing a viable and on-the-rails method for dealing with this scenario (i.e. when changing the base class is not an option). It suggests providing XmlAttributeOverrides to the XmlSerializer.

Using your provided object model, the following code illustrates the use of this. It works by explicitly telling the XmlSerializer to ignore the hidden property in the base class, in this case Model.Template.

using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;

class Program
{
    static void Main(string[] args)
    {
        ConcreteTemplate ct = new ConcreteTemplate() { SomeProperty = "hello" };
        ConcreteGenericModel cgm = new ConcreteGenericModel(ct);

        XmlAttributeOverrides attrOverides = new XmlAttributeOverrides();
        XmlAttributes attrs = new XmlAttributes() { XmlIgnore = true };
        attrOverides.Add(typeof(Model), "Template", attrs);

        Type[] extraTypes = new Type[0];
        XmlSerializer serializer = new XmlSerializer(typeof(ConcreteGenericModel), attrOverides, extraTypes, null, null);

        StringBuilder sb = new StringBuilder();
        using (StringWriter writer = new StringWriter(sb))
            serializer.Serialize(writer, cgm);
        string serializedClass = sb.ToString();

        Console.WriteLine(serializedClass);

        ConcreteGenericModel deserializedCgm;
        using (StringReader reader = new StringReader(serializedClass))
            deserializedCgm = (ConcreteGenericModel)serializer.Deserialize(reader);

        Console.ReadLine();
    }
}

public abstract class Template
{
    // Some properties and methods defined
    public virtual string SomeProperty { get; set; }
}

public abstract class Template<TTemplate> : Template where TTemplate : Template
{
    // No new properties defined, but methods overriden
}

public class ConcreteTemplate : Template { }

public abstract class Model
{
    public Model() { }
    public Template Template { get; set; }
    public Model(Template t) { Template = t; }
    // More properties and methods
}

public class ConcreteModel : Model
{
    public ConcreteModel(Template t) : base(t) { }
}

public abstract class Model<TModel, TTemplate> : Model
    where TModel : Model
    where TTemplate : Template
{
    public Model() { }
    public new TTemplate Template { get { return (TTemplate)base.Template; } set { base.Template = value; } }
    public Model(TTemplate t) : base(t) { }
    // Override some methods but no new properties
}

public class ConcreteGenericModel : Model<ConcreteModel, ConcreteTemplate>
{
    public ConcreteGenericModel() { }
    public ConcreteGenericModel(ConcreteTemplate t) : base(t) { }
}
cokeman19
  • 2,405
  • 1
  • 25
  • 40
  • Thanks! Looks like this is going to work, although I typically pass in `extraTypes` to the `XmlSerializer` constructor, which means I also need an `XmlRoot` and default namespace. Everything is serializing OK, just having trouble deserializing now... – mmathis Mar 11 '16 at 20:51
  • 1
    You can pass null for the `root` and `defaultNamespace` parameters that you don't need. This is what the other constructor overloads do internally. I've updated the example to show using this overload. – cokeman19 Mar 12 '16 at 04:44
  • Sorry for bumping an old question, but what happens if I need both parent class and child class to serialize? – tyteen4a03 Dec 06 '17 at 10:44
  • Serialization would likely have to be explicitly controlled/configured separately for the parent and child classes. If you have a specific example, could you create a new question, referencing this one, if relevant? – cokeman19 Dec 07 '17 at 03:11