I would like to be able to XML serialize a subclass, but exclude its base class from the serialization.
Here\'s my class (configured for DataObjects.NET):
First off, the base class doesn't need a parameterless constructor as long as the derived class, MyClass
, has one. Thus the following derived class can be round-tripped from and to XML:
public class BaseClassWithNoParameterlessConstructor
{
public BaseClassWithNoParameterlessConstructor(string value)
{
this.Value = value;
}
public string Value { get; set; }
}
public class DerivedClassWithAParameterlessConstructor : BaseClassWithNoParameterlessConstructor
{
public DerivedClassWithAParameterlessConstructor()
: this(string.Empty) // Some plausible default value
{
}
public DerivedClassWithAParameterlessConstructor(string value)
: base(value)
{
}
public string Name { get; set; }
}
However, the presence of the property public int Id { get; private set; }
will cause the XmlSerializer
constructor to throw an exception, because Id
cannot be set publicly. You need to mark it with [XmlIgnore] or make the setter public. Or if you really don't want to make the setter public, and never intend to deserialize your xml, you could do something evil like:
[XmlIgnore]
public int Id { get; private set; }
[XmlElement("Id")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int DummyIdWIthFakeSetter { get { return Id; } set { /* DO NOTHING */ } }
Secondly, to suppress a specific property in a base class, named XXX
for instance, you can introduce a method named bool ShouldSerializeXXX()
in your derived class, and make it return false. This will suppress output of that property, but will need to be done property-by-property. See here for details.
If you want to ignore all properties declared in base classes wholesale, you can construct an XmlSerializer with given XmlAttributeOverrides that specify base class property names to ignore, as is shown here. However, there are some issues of which to be aware:
You can only add an XmlAttributes override to a property in the type in which the property is actually declared. Doing so will apply to that property in that type and all derived types. This will do what you want - but maybe more than you want. If you try to override the property just in a derived type, the override is ignored. I'm not sure this is documented, but I have found it to be true. Thus if you were serializing an object graph containing both base and derived classes, and wanted to serialize base class properties when "standalone" but not when subclassed, this technique would produce bad results. But in your case it should be OK because your base classes cannot be serialized standalone anyway.
You must explicitly cache your XmlSerializer
in a hash table to avoid memory leaks, as is explained here.
Thus, if you know all the base types whose properties should be ignored, you can use the following to manufacture a serializer that serializes subclasses and ignores base class properties:
public static class XmlSerializationIgnoreOverrideCreator<T>
{
static Dictionary<HashSet<Type>, XmlSerializer> table = new Dictionary<HashSet<Type>, XmlSerializer>(HashSet<Type>.CreateSetComparer());
public static XmlSerializer CreateOverrideSerializer()
{
return CreateOverrideSerializer(new Type[] { typeof(T).BaseType });
}
public static XmlSerializer CreateOverrideSerializer(IEnumerable<Type> typesWithPropertiesToIgnore)
{
var set = new HashSet<Type>(typesWithPropertiesToIgnore);
if (set.Count == 0)
return new XmlSerializer(typeof(T));
lock (table)
{
XmlSerializer serializer;
if (table.TryGetValue(set, out serializer))
return serializer;
var xOver = new XmlAttributeOverrides();
foreach (var type in set)
{
IgnoreAllProperties(type, xOver);
}
table[set] = serializer = new XmlSerializer(typeof(T), xOver);
return serializer;
}
}
static void IgnoreAllProperties(Type type, XmlAttributeOverrides xOver)
{
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string))
return;
var attrs = new XmlAttributes() { XmlIgnore = true };
foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (xOver[type, property.Name] == null) // Check to see if overrides for this base type were already set.
xOver.Add(type, property.Name, attrs);
var baseType = type.BaseType;
if (baseType != type)
IgnoreAllProperties(baseType, xOver);
}
}