Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XML serialize class with new property which hides inherited member

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?

like image 702
mmathis Avatar asked Mar 10 '16 23:03

mmathis


1 Answers

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) { }
}
like image 76
cokeman19 Avatar answered Nov 15 '22 04:11

cokeman19