My question is so simple.
Imagine we have a Foo class. and has a T property.
public class Foo<T>
{
public T Property { get; set; }
}
I want to implement in it IXmlSerializable and my problem is I have no idea to imitate the standard XmlSerialization. I really have no idea about how to write this property like the standart XmlSerlalization.
UPDATE: I did this and it's not working
public class Foo<T> : IFoo
where T : IXmlSerializable
{
public T Value { get; set; }
}
public class FooA : Foo<string> { }
public class FooB : Foo<int> { }
public class FooC : Foo<List<Double>> { }
When I serialized this classes (A, B and C). I wanna have something like this:
Using first class:
<FooA>
<Value>StringDemo</Value>
</FooA>
Using second class:
<FooB>
<Value>2</Value>
</FooB>
Using third class:
<FooC>
<Value>
<ArrayOfDouble xsi:..>
<Double>3.1416</Double>
<Double>4.2</Double>
</ArrayOfDouble>
</Value>
</FooC>
Something like this is what I wanted, I don't want to implement in all of this a custom XmlSerializer, I mean the default which the standard serialization use.
You would need to implement IXmlSerializable in Foo<T>, and T (using where T : IXmlSerializable. The implementation in Foo<T> would then be responsible for writing a wrapper element with type information (in WriteXml) and call WriteXml on T, and recreate the instance from the type information (in ReadXml) and call ReadXml on T.
Basic types like String and Double is not IXmlSerializable, so you cant have where T : IXmlSerializable if you want to support them. You can also provide default implementations of ReadXml and WriteXml. This works since you need to create a Foo<T> before calling ReadXml, forcing you to know T.
public class Foo<T> : IXmlSerializable {
public T Value { get; set; }
XmlSchema IXmlSerializable.GetSchema() {
return GetSchema();
}
protected virtual XmlSchema GetSchema() {
return null;
}
public virtual void ReadXml(XmlReader reader) {
reader.ReadStartElement();
var xmlSerializer = new XmlSerializer(typeof(T));
Value = (T)xmlSerializer.Deserialize(reader);
reader.ReadEndElement();
}
public virtual void WriteXml(XmlWriter writer) {
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(writer, Value);
}
}
Related
I realize this looks to be an exact duplicate of Using XmlSerializer to serialize derived classes, but I cannot figure out how to get this working following the guidance from that same question:
using System;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace xmlSerializerLab
{
public class Utf8StringWriter : System.IO.StringWriter
{
public override Encoding Encoding => Encoding.UTF8;
}
[XmlRoot(ElementName = "Query", Namespace = "http://www.opengis.net/wfs")]
public class Query
{
[XmlElement(ElementName = "Filter", Namespace = "http://www.opengis.net/ogc")]
public Filter Filter { get; set; }
}
[XmlInclude(typeof(PropertyIsOpFilter))]
[XmlInclude(typeof(PropertyIsEqualToFilter))]
[XmlInclude(typeof(OpFilterBase))]
[XmlInclude(typeof(LiteralFilter))]
[XmlInclude(typeof(Query))]
[Serializable]
public class Filter
{
[XmlElement]
public Filter And { get; set; }
}
public class PropertyIsOpFilter : Filter, IXmlSerializable
{
public Filter LeftOp { get; set; }
public Filter RightOp { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader) { }
public void WriteXml(XmlWriter writer)
{
Program.ToXml(LeftOp, writer);
Program.ToXml(RightOp, writer);
}
}
[XmlRoot("IsEqualTo")]
public class PropertyIsEqualToFilter : PropertyIsOpFilter { }
public class OpFilterBase : Filter, IXmlSerializable
{
public string Op { get; set; }
public object Value { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader) { }
public void WriteXml(XmlWriter writer)
{
if (!String.IsNullOrEmpty(Op))
{
writer.WriteStartElement(Op);
writer.WriteValue(Value);
writer.WriteEndElement();
}
else
{
writer.WriteValue(Value);
}
}
}
public class LiteralFilter : OpFilterBase { }
class Program
{
public static void ToXml(Object o, XmlWriter writer)
{
var inputSerializer = new XmlSerializer(o.GetType(), new Type[] {
typeof(Filter),
typeof(PropertyIsOpFilter),
typeof(PropertyIsEqualToFilter),
typeof(OpFilterBase),
typeof(LiteralFilter),
typeof(Query)
});
inputSerializer.Serialize(writer, o);
}
public static string ToXml(Object o)
{
var inputSerializer = new XmlSerializer(o.GetType());
using (var writer = new Utf8StringWriter())
{
using (var xmlWriter = new XmlTextWriter(writer))
{
ToXml(o, xmlWriter);
}
return writer.ToString();
}
}
static void Main(string[] args)
{
Filter o = new PropertyIsEqualToFilter()
{
LeftOp = new LiteralFilter()
{
Value = 1
},
RightOp = new LiteralFilter()
{
Value = 1
}
};
var query = new Query()
{
Filter = o
};
Console.WriteLine(ToXml(query));
Console.ReadLine();
}
}
}
It results in this exception:
InvalidOperationException: The type
xmlSerializerLab.PropertyIsEqualToFilter may not be used in this
context. To use xmlSerializerLab.PropertyIsEqualToFilter as a
parameter, return type, or member of a class or struct, the parameter,
return type, or member must be declared as type
xmlSerializerLab.PropertyIsEqualToFilter (it cannot be object).
Objects of type xmlSerializerLab.PropertyIsEqualToFilter may not be
used in un-typed collections, such as ArrayLists.
As far as I can tell, I need the IXmlSerializable on the PropertyIsOpFilter and OpFilterBase because I'm trying to target a specific XML format described by this schema. But I'm finding that I also have to make the Query class IXmlSerializable.
Here is a sample XML document that I'd like to be able to produce from the model:
<GetFeature xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
service="WFS"
version="1.1.0"
maxFeatures="0" xmlns="http://www.opengis.net/wfs">
<ResultType>Results</ResultType>
<OutputFormat>text/gml; subtype=gml/3.1.1</OutputFormat>
<Query
d2p1:srsName="EPSG:4326" xmlns:d2p1="http://www.opengis.net/ogc">
<d2p1:Filter>
<d2p1:IsEqualTo>
<d2p1:PropertyName>Prop1</d2p1:PropertyName>
<d2p1:Literal>1</d2p1:Literal>
</d2p1:IsEqualTo>
</d2p1:Filter>
</Query>
</GetFeature>
By making the Query class IXmlSerializable and writing a good bit of WriteXml and ReadXml logic I can get it to work, but I'd expect it to work without having to do all that since the XmlRoot and XmlAttribute and XmlElement tags should give enough information to the serializer for it to know which class to instantiate based on the tag name (match ElementName) and certainly how to serialize based on the attributes.
The problem you are seeing can be reproduced with the following minimal example:
public class BaseClass
{
}
public class DerivedClass : BaseClass, IXmlSerializable
{
#region IXmlSerializable Members
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader) { throw new NotImplementedException(); }
public void WriteXml(XmlWriter writer) { }
#endregion
}
Using the serialization code:
BaseClass baseClass = new DerivedClass();
using (var textWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(textWriter))
{
var serializer = new XmlSerializer(typeof(BaseClass), new Type[] { typeof(DerivedClass) });
serializer.Serialize(xmlWriter, baseClass);
}
Console.WriteLine(textWriter.ToString());
}
The following exception is thrown (sample fiddle #1):
System.InvalidOperationException: There was an error generating the XML document.
---> System.InvalidOperationException: The type DerivedClass may not be used in this context. To use DerivedClass as a parameter, return type, or member of a class or struct, the parameter, return type, or member must be declared as type DerivedClass (it cannot be object). Objects of type DerivedClass may not be used in un-typed collections, such as ArrayLists.
This is among the most unhelpful exception messages I have seen from XmlSerializer. To understand the exception, you need to understand how XmlSerializer handles polymorphism via the [XmlInclude] mechanism. If I remove IXmlSerializable from DerivedClass, the following XML is generated (fiddle #2):
<BaseClass xsi:type="DerivedClass" />
Notice the xsi:type type attribute? That is a w3c standard attribute that XmlSerializer uses to explicitly assert the type of a polymorphic element; it is documented here. When XmlSerializer is deserializing a polymorphic type to which [XmlInclude] attributes have been applied (either statically or through the constructor you are using), it will look for the xsi:type attribute to determine the actual type to construct and deserialize.
It is this, apparently, which conflicts with IXmlSerializable. A type which implements this interface should completely control its XML reading and writing. However, by parsing and interpreting the xsi:type attribute, XmlSerializer has already begun automatic deserialization, and so throws an exception due to the inconsistent deserialization strategies of the base and derived types.
What's more, adding IXmlSerializable to the base type doesn't really fix the problem either If you do so, the xsi:type attribute is never written, and later, when ReadXml() is called, an object of the base type will get unconditionally constructed, as shown in fiddle #3.
(It's conceivable that Microsoft could have implemented a special case where XmlSerializer begins automatic deserialization, then "backs off" and hands the task over to ReadXml() when an IXmlSerializable polymorphic type is encountered and constructed. But, they did not.)
The solution would seem to be to serialize your Filter types automatically using the [XmlInclude] mechanism. In fact I don't see any reason you need to use IXmlSerializable, and was able to serialize your model successfully by removing IXmlSerializable completely and making some minor changes to namespaces:
public static class XmlNamespaces
{
public const string OpengisWfs = "http://www.opengis.net/wfs";
}
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class Query
{
public Filter Filter { get; set; }
}
[XmlInclude(typeof(PropertyIsOpFilter))]
[XmlInclude(typeof(OpFilterBase))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class Filter
{
[XmlElement]
public Filter And { get; set; }
}
[XmlInclude(typeof(PropertyIsEqualToFilter))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class PropertyIsOpFilter : Filter
{
public Filter LeftOp { get; set; }
public Filter RightOp { get; set; }
}
[XmlRoot("IsEqualTo", Namespace = XmlNamespaces.OpengisWfs)]
public class PropertyIsEqualToFilter : PropertyIsOpFilter { }
[XmlInclude(typeof(LiteralFilter))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class OpFilterBase : Filter
{
public string Op { get; set; }
public object Value { get; set; }
}
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class LiteralFilter : OpFilterBase { }
Notes:
For the [XmlInclude] mechanism to work, all the included types apparently must be in the same XML namespace as the base type. To ensure this I added [XmlRoot(Namespace = XmlNamespaces.OpengisWfs)] to all the Filter subtypes.
The [XmlInclude(typeof(DerivedType))] attributes can be added either to their immediate parent type or to the lowest common base type. In the code above I added the attributes to the immediate parent types so that members of an intermediate type could be serialized successfully, e.g.:
public class SomeClass
{
PropertyIsOpFilter IsOpFilter { get; set; }
}
Consider marking intermediate types that cannot be instantiated as abstract, e.g. public abstract class Filter. Consider marking types that are "most derived" as sealed, e.g. public sealed class LiteralFilter
If you use the new XmlSerializer(Type, Type []) constructor, you must statically cache the serializer to avoid a severe memory leak, as explained here. It's not necessary in my solution but you are using it in your question.
Sample fiddle #4 showing that the following XML is generated successfully:
<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wfs">
<Filter xsi:type="PropertyIsEqualToFilter">
<LeftOp xsi:type="LiteralFilter">
<Value xsi:type="xsd:int">1</Value>
</LeftOp>
<RightOp xsi:type="LiteralFilter">
<Value xsi:type="xsd:int">1</Value>
</RightOp>
</Filter>
</Query>
I have a class that I need to serialize/deserialize, and I'm half way there - I have serialization functional, resulting in the below XML. However, since I'm implementing IXmlSerializable myself, I'm uncertain what an implementation of ReadXml should look like, given that SomeGenericClass<T> was serialized using attribute-based flagging rather than an explicit implementation if IXmlSerializable
<?xml version="1.0" encoding="utf-16"?>
<FooContainer FooName="DoSomething">
<SomeGenericClassOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="Foobar" Name="firstParam" Description="First Paramater Serialized" />
<SomeGenericClassOfInt32 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="10000" Name="nextParam" Description="Second Serialized parameter" />
</FooContainer>
Which I want to serialize back into an instance of:
public class FooContainer : IList<ISomeGenericClassBase>, IXmlSerializable
{
public string FooName {get;set;}
void IXmlSerializable.WriteXml(XmlWriter writer) {
var serializer = XmlSerializer.FromTypes(new Type[]{SomeGenericBaseClass})[0];
this
.Select(item=>SomeGenericClassBase.ConvertToMe(item))
.ToList()
.ForEach(item=>serializer.Serialize(writer, item));
}
// IList Implementation omitted - wraps a private List<ISomeGenericClassBase>
}
Where the list will contain instances along these lines:
public interface ISomeGenericClassBase
{
}
public interface ISomeGenericBaseClass<T> : ISomeGenericBaseClass
{
}
public class SomeGenericClassBase : ISomeGenericClassBase
{
public static SomeGenericClassBase ConvertToMe(ISomeGenericClassBase target) {
return new SomeGenericClassBase() {Property1 = target.Property1; Property2 = target.Property2}
}
public static ISomeGenericBaseClass ExpantToTyped(SomeGenericClassBase target) {
// Implementation omitted - converts a base class instance to a generic instance by working out the generic type from saved data and reconstructing
}
}
public class SomeGenericClass<T> : SomeGenericClassBase, ISomeGenericBaseClass<T>
{
[XmlAttribute]
public string Name {get;set;}
[XmlAttribute]
public string Description{get;set;}
[XmlAttribute]
public T Value {get;set;}
[XmlElement("")]
public T[] ValidOptions {get;set;}
}
EDIT: Expanded the implementation - realised as it was, it didn't illustrate the problem correctly
Core issue is that I want to be able to serialize items that only implement the interface, even if I only get back SomeGenericClassBase instances. Per the approach used in the ExpandToTyped method, I'm expecting consumers of the class to save sufficient data in their implementations that allow the resulting classes to be converted back into their original form as required. So yes, there's a loss of fidelity, but it's one I can live with in exchange for the flexibility of using a list of interfaces instead of a list of base classes.
One solution is to sidestep the issue (IXmlSerializable.ReadXml looks quite painful anyway, e.g. for collections). What I eventually did is scrap IXmlSerializable, and instead generate a class along the lines of the below.
Please note that whilst this approach works, it's currently quite error prone if the serializable instance is used for anything other than serialization - synchronization is maintained ONLY when SerializationTarget is set or retrieved. When it's set, we convert existing parameters to appropriate instances and add them to a serializable list. When it's retrieved, if it's null, we inflate from whatever was in the current value.
However, if FooContainer changes after the creation of this object, it won't maintain that synchronization and what gets serialized will be out of date. This is largely because I'm lazy and don't want to implement IList<SomeGenericClassBase> again to override the Add and Remove methods (though this would be the more robust approach).
public class FooContainerSerializable
{
public FooContainerSerializable() {}
public FooContainerSerializable(FooContainer serializationTarget)
{
this.SerializationTarget = serializationTarget;
}
[XmlIgnore]
public FooContainer SerializationTarget
{
get {
if (_SerializationTarget == null)
{
_SerializationTarget = new FooContainer();
// Copy across extant collection properties here
this.Parameters.ForEach(item=>_SerializationTarget.Add(item));
}
return _SerializationTarget;
}
set {
// Synchronize this entity's entries here
_SerializationTarget = value;
_SerializationTarget.ForEach(item=>this.Parameters.Add(item.Deflate()));
}
}
private FooContainer _SerializationTarget;
[XmlElement]
public string FooName {
get {return this.SerializationTarget.FooName;}
set {this.SerializationTarget.FooName = value;}
}
[XmlElement]
public List<SomeGenericClassBase> Parameters {
get {return _Parameters ?? (_Parameters = new List<SomeGenericClassBase>());}
set {_Parameters = value;}
}
}
Here is another option if you are willing to use an abstract class instead of an interface in your collection definition. You'd also need to declare all the derived types of SomeGenericClassBase using XmlInclude attributes. I'm thinking this wouldn't be too bad if there are just a handful of types you'd use with this class.
[XmlRoot(ElementName = "FooContainer")]
public class FooContainer : List<SomeGenericClassBase>
{
[XmlAttribute]
public string FooName { get; set; }
}
[XmlInclude(typeof(SomeGenericClass<string>))]
[XmlInclude(typeof(SomeGenericClass<int>))]
public abstract class SomeGenericClassBase
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Description { get; set; }
}
public class SomeGenericClass<T> : SomeGenericClassBase
{
[XmlAttribute]
public T Value { get; set; }
[XmlElement]
public T[] ValidOptions { get; set; }
}
class Class1
{
public static void Run()
{
var f = new FooContainer()
{
new SomeGenericClass<string> { Name = "firstParam", Description = "First Paramater Serialized", Value = "Foobar"},
new SomeGenericClass<int> { Name = "nextParam", Description = "Second Serialized parameter", Value = 10000 }
};
f.FooName = "DoSomething";
XmlSerializer serializer = new XmlSerializer(f.GetType());
StringBuilder sb = new StringBuilder();
// Serialize
using (StringWriter writer = new StringWriter(sb))
{
serializer.Serialize(writer, f);
}
Console.WriteLine(sb);
// Deserialize
using(StringReader reader = new StringReader(sb.ToString()))
{
FooContainer f2 = (FooContainer)serializer.Deserialize(reader);
}
}
}
This would serialize to the following XML:
<?xml version="1.0" encoding="utf-16"?>
<FooContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SomeGenericClassBase xsi:type="SomeGenericClassOfString" Name="firstParam" Description="First Paramater Serialized" Value="Foobar" />
<SomeGenericClassBase xsi:type="SomeGenericClassOfInt32" Name="nextParam" Description="Second Serialized parameter" Value="10000" />
</FooContainer>
Deserialization maintains full fidelity.
For a current project I am trying to make have a generic XML creator using the XmlSerializer class - I need a certain element in one of the classes to have the ElementName set based on the type of class that the creator is in in this context that is simple enough. Here is an example:
public abstract class ElementTypeBase
{
public abstract string ElementName { get; }
}
public class ElementTypeA : ElementTypeBase
{
public override string ElementName
{
get { return "ElementA"; }
}
}
Then pass this to my XML object class which will be used for the XmlSerializer but I want the ElementName to be specific to the type.
public class XMLObject<T> where T : ElementTypeBase
{
[XmlElement(Activator.CreateInstance<T>().ElementName)]
public string SomeElement;
}
I thought I would be able to do that but get:
An attribute argument must be a constant expression, typeof expression
or array creation expression of an attribute parameter type
So I thought that I could override ToString() but this doesn't work, I was thinking of using constants but it feels dirty. Any other suggestions?
You can do that through the XmlAttributeOverrides class like shown below.
However, you'll need to cache the XmlSerializer instance and I wouldn't recommend this approach for your problem.
when you say that you have one element that is gonna be different for each inherited object, I'd suggest to put this element in the inherited class, not the generic one, and hard-code its XmlElement.ElementName.
using System;
using System.Xml.Serialization;
public class Program
{
static void Main(string[] args)
{
XmlSerializer serializer =
new XmlSerializer(typeof(XMLObject<MyElement>),
XMLObject<MyElement>.Overrides);
serializer.Serialize(Console.Out,
new XMLObject<MyElement>() { SomeElement = "value" });
Console.ReadLine();
}
}
public class XMLObject<T> where T : ElementTypeBase, new()
{
public static XmlAttributeOverrides Overrides { get; private set; }
static XMLObject()
{
Overrides = new XmlAttributeOverrides();
Overrides.Add(typeof(XMLObject<T>), "SomeElement",
new XmlAttributes
{
XmlElements =
{
new XmlElementAttribute(new T().ElementName)
}
});
}
public string SomeElement { get; set; }
}
public abstract class ElementTypeBase
{
public abstract string ElementName { get; }
}
public class MyElement : ElementTypeBase
{
public override string ElementName
{
get { return "ElementA"; }
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to serialize an IList<T>?
I wish to serialize an class (let's call it S) that contains a property of the type IList<T> where T is another class which I have defined. I get an exception when I attempted to serialize an instance of class S to XML. This is understandable as the XmlSerializer doesn't know which concrete class to use. Is there a way, hopefully using attributes, to specify which concrete class to instantiate when serializing/deserializing an instance. My implementation of class S creates a instance of the List<T> class. Here is some code to illustrate my example:
using System;
using System.Xml.Serialization;
using System.IO;
[Serializable]
public class T { }
[Serializable]
public class S
{
public IList<T> ListOfTs { get; set; }
public S()
{
ListOfTs = new List<T>();
}
}
public class Program
{
public void Main()
{
S s = new S();
s.ListOfTs.Add(new T());
s.ListOfTs.Add(new T());
XmlSerializer serializer = new XmlSerializer(typeof(S));
serializer.Serialize(new StringWriter(), s);
}
}
I'm hoping there's an attribute I can place above the ListOfTs definition that says to the serialize, "assume an instance of List<T> when serializing/deserializing".
Change the
public IList<T> ListOfTs { get; set; }
to
public List<T> ListOfTs { get; set; }
Then it will work.
I think there's 2 ways that are generally approached for this kind of problem.
One would be to use DataContractSerializer, but since you're already using XmlSerializer, you probably don't want to do that.
The "alternative" is to have a property that is used specifically for the purpose of serialization, and to ignore the IList during serialization.
[Serializable]
public class S
{
IList<T> _listofTs;
[XmlIgnore]
public IList<T> ListOfTs { get _listofTs; set _listofTs = value; }
[XmlElement(Name="Not Exactly the Greatest Solution!")]
public List<T> ListOfTs { get _listofTs; set _listofTs = value; }
public S()
{
ListOfTs = new List<T>();
}
}
You can implement the IXmlSerializable interface and implement the serialization logic manually using Xml readers and writers.
I am trying to create a base class where I can inherit from it (to add properties to the derived classes) and the utilized the Load and Save methods from the base class. I find myself writing the Load and Save over and over and I'd like to apply some DRY to it...
namespace Common
{
using System;
using System.IO;
using System.Xml.Serialization;
public abstract class ApplicationSettings
{
protected ApplicationSettings()
{
}
public static ApplicationSettings Load(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamReader reader = new StreamReader(fileName))
{
ApplicationSettings param = (ApplicationSettings)serializer.Deserialize(reader);
reader.Close();
return param;
}
}
public void Save(string fileName)
{
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamWriter writer = new StreamWriter(fileName))
{
serializer.Serialize(writer, this);
writer.Close();
}
}
}
}
Given this abstract class, I then want to derive a class such as:
namespace Common
{
using System;
public class ApplicationParameters : ApplicationSettings
{
public ApplicationParameters()
{
}
public string AuthorizationCode
{
get;
set;
}
public string ReferenceNumber
{
get;
set;
}
}
}
For the Derived class, I should be able to do something like
ApplicationParameters parameters =
ApplicationParmeters.Load("settings.xml");
However, in the implementation above, an compiler error occurs when I attempt to cast the ApplicationSettings to the ApplicationParameters class when I call the Load method in the base class.
Is there a way to do this?
Try replacing typeof(ApplicationSettings) with GetType().
Using this mechanism you will also tell the serializer that ApplicationParameters is a child class of ApplicationSettings. You do this via XmlInclude
[XmlInclude(typeof(ApplicationParameters))]
class ApplicationSettings
The latter is a requirements of the serializer because otherwise it won't know what class to instantiate.
Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, DataContractSerializer is recommended
See here, for example
Make the top level class generic so that the Save/Load methods can support multiple types:
public abstract class ApplicationSettings<T>
{
public static T Load(string xml){ // Implementation }
public static void Save (T obj) { // Implementation }
}
public class ApplicationParameters : ApplicationSettings<ApplicationParameters>
{
}
Or you could just make the static methods themselves generic:
public abstract class ApplicationSettings
{
public static T Load<T>(string xml){ // implementation }
public static void Save<T>(T obj){ // implementation }
}
You will now notice that the Save/Load methods from the abstract parent class are strongly typed to the child so that the following line will work as expected:
ApplicationParameters parameters = ApplicationParameters.Load("settings.xml");
or
ApplicationParameters parameters =
ApplicationSettings.Load<ApplicationParameters>("settings.xml");
Depending on which method you use.
How about a constructor in your ApplicationParameters class that takes an ApplicationSettings as an argument and copy the shared properties from one to another? And then just set the not shared properties to be null or the default...